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.
- package/README.md +101 -100
- package/dist/cjs/app-chips_5.cjs.entry.js +158 -0
- package/dist/cjs/app-chips_5.cjs.entry.js.map +1 -0
- package/dist/cjs/app-globals-Chb-oJtg.js +34 -0
- package/dist/cjs/app-globals-Chb-oJtg.js.map +1 -0
- package/dist/cjs/index-By1scwl6.js +25542 -0
- package/dist/cjs/index-By1scwl6.js.map +1 -0
- package/dist/cjs/index-CgmLNwZO.js +21460 -0
- package/dist/cjs/index-CgmLNwZO.js.map +1 -0
- package/dist/cjs/index.cjs.js +5 -483
- package/dist/cjs/index.cjs.js.map +1 -1
- package/dist/cjs/llm-testrunner.cjs.js +6 -4
- package/dist/cjs/llm-testrunner.cjs.js.map +1 -1
- package/dist/cjs/loader.cjs.js +5 -3
- package/dist/collection/collection-manifest.json +8 -3
- package/dist/collection/components/error-message/error-message.css +34 -0
- package/dist/collection/components/error-message/error-message.js +2 -2
- package/dist/collection/components/error-message/error-message.js.map +1 -1
- package/dist/collection/components/llm-test-runner/header/llm-test-runner-header.css +60 -0
- package/dist/collection/components/llm-test-runner/header/llm-test-runner-header.js +18 -0
- package/dist/collection/components/llm-test-runner/header/llm-test-runner-header.js.map +1 -0
- package/dist/collection/components/llm-test-runner/llm-test-runner.css +17 -657
- package/dist/collection/components/llm-test-runner/llm-test-runner.import-export.test.js +253 -0
- package/dist/collection/components/llm-test-runner/llm-test-runner.import-export.test.js.map +1 -0
- package/dist/collection/components/llm-test-runner/llm-test-runner.js +191 -200
- package/dist/collection/components/llm-test-runner/llm-test-runner.js.map +1 -1
- package/dist/collection/components/llm-test-runner/test-cases/actions/row-actions.css +28 -0
- package/dist/collection/components/llm-test-runner/test-cases/actions/row-actions.js +6 -0
- package/dist/collection/components/llm-test-runner/test-cases/actions/row-actions.js.map +1 -0
- package/dist/collection/components/llm-test-runner/test-cases/evaluation/evaluation-summary.css +67 -0
- package/dist/collection/components/llm-test-runner/test-cases/evaluation/evaluation-summary.js +5 -0
- package/dist/collection/components/llm-test-runner/test-cases/evaluation/evaluation-summary.js.map +1 -0
- package/dist/collection/components/llm-test-runner/test-cases/llm-test-case-row.css +42 -0
- package/dist/collection/components/llm-test-runner/test-cases/llm-test-case-row.js +39 -0
- package/dist/collection/components/llm-test-runner/test-cases/llm-test-case-row.js.map +1 -0
- package/dist/collection/components/llm-test-runner/test-cases/llm-test-cases.css +39 -0
- package/dist/collection/components/llm-test-runner/test-cases/llm-test-cases.js +7 -0
- package/dist/collection/components/llm-test-runner/test-cases/llm-test-cases.js.map +1 -0
- package/dist/collection/components/llm-test-runner/test-cases/output/response-output.css +51 -0
- package/dist/collection/components/llm-test-runner/test-cases/output/response-output.js +5 -0
- package/dist/collection/components/llm-test-runner/test-cases/output/response-output.js.map +1 -0
- package/dist/collection/global/env.js +3 -1
- package/dist/collection/global/env.js.map +1 -1
- package/dist/collection/index.js.map +1 -1
- package/dist/collection/lib/evaluation/constants.js +14 -0
- package/dist/collection/lib/evaluation/constants.js.map +1 -0
- package/dist/collection/lib/evaluation/evaluation-engine.js +45 -45
- package/dist/collection/lib/evaluation/evaluation-engine.js.map +1 -1
- package/dist/collection/lib/evaluation/evaluation-service.js +33 -0
- package/dist/collection/lib/evaluation/evaluation-service.js.map +1 -0
- package/dist/collection/lib/evaluation/evaluators/bleu/bleu-evaluator.js +116 -0
- package/dist/collection/lib/evaluation/evaluators/bleu/bleu-evaluator.js.map +1 -0
- package/dist/collection/lib/evaluation/evaluators/bleu/tests/bleu.test.js +352 -0
- package/dist/collection/lib/evaluation/evaluators/bleu/tests/bleu.test.js.map +1 -0
- package/dist/collection/lib/evaluation/evaluators/exact/exact.js +44 -0
- package/dist/collection/lib/evaluation/evaluators/exact/exact.js.map +1 -0
- package/dist/collection/lib/evaluation/evaluators/rouge1-evaluator.js +88 -0
- package/dist/collection/lib/evaluation/evaluators/rouge1-evaluator.js.map +1 -0
- package/dist/collection/lib/evaluation/evaluators/rougeL-evaluator.js +82 -0
- package/dist/collection/lib/evaluation/evaluators/rougeL-evaluator.js.map +1 -0
- package/dist/collection/lib/evaluation/evaluators/rougeL-evaluator.test.js +326 -0
- package/dist/collection/lib/evaluation/evaluators/rougeL-evaluator.test.js.map +1 -0
- package/dist/collection/lib/evaluation/evaluators/semantic/SemanticEvaluator.js +69 -0
- package/dist/collection/lib/evaluation/evaluators/semantic/SemanticEvaluator.js.map +1 -0
- package/dist/collection/lib/evaluation/evaluators/semantic/evaluate-keywords.js +56 -0
- package/dist/collection/lib/evaluation/evaluators/semantic/evaluate-keywords.js.map +1 -0
- package/dist/collection/lib/evaluation/evaluators/semantic/index.js +7 -0
- package/dist/collection/lib/evaluation/evaluators/semantic/index.js.map +1 -0
- package/dist/collection/lib/evaluation/evaluators/semantic/model-loader.js +19 -0
- package/dist/collection/lib/evaluation/evaluators/semantic/model-loader.js.map +1 -0
- package/dist/collection/lib/evaluation/evaluators/semantic/similarity-utils.js +16 -0
- package/dist/collection/lib/evaluation/evaluators/semantic/similarity-utils.js.map +1 -0
- package/dist/collection/lib/evaluation/evaluators/semantic/tests/evaluate-keywords.test.js +65 -0
- package/dist/collection/lib/evaluation/evaluators/semantic/tests/evaluate-keywords.test.js.map +1 -0
- package/dist/collection/lib/evaluation/evaluators/semantic/text-utils.js +5 -0
- package/dist/collection/lib/evaluation/evaluators/semantic/text-utils.js.map +1 -0
- package/dist/collection/lib/evaluation/index.js.map +1 -1
- package/dist/collection/lib/evaluation/rouge1-evaluator.test.js +117 -0
- package/dist/collection/lib/evaluation/rouge1-evaluator.test.js.map +1 -0
- package/dist/collection/lib/evaluation/types.js.map +1 -1
- package/dist/collection/lib/file/file-download.js +18 -0
- package/dist/collection/lib/file/file-download.js.map +1 -0
- package/dist/collection/lib/file/file-reader.js +14 -0
- package/dist/collection/lib/file/file-reader.js.map +1 -0
- package/dist/collection/lib/form/components/app-chips.css +97 -0
- package/dist/collection/lib/form/components/app-chips.js +155 -0
- package/dist/collection/lib/form/components/app-chips.js.map +1 -0
- package/dist/collection/lib/form/components/app-select.css +28 -0
- package/dist/collection/lib/form/components/app-select.js +101 -0
- package/dist/collection/lib/form/components/app-select.js.map +1 -0
- package/dist/collection/lib/form/components/app-textarea.css +38 -0
- package/dist/collection/lib/form/components/app-textarea.js +126 -0
- package/dist/collection/lib/form/components/app-textarea.js.map +1 -0
- package/dist/collection/lib/form/form-builder.js +171 -0
- package/dist/collection/lib/form/form-builder.js.map +1 -0
- package/dist/collection/lib/form/schema/base-input-field-config.js +2 -0
- package/dist/collection/lib/form/schema/base-input-field-config.js.map +1 -0
- package/dist/collection/lib/form/schema/form-control-config.js +2 -0
- package/dist/collection/lib/form/schema/form-control-config.js.map +1 -0
- package/dist/collection/lib/form/schema/index.js +8 -0
- package/dist/collection/lib/form/schema/index.js.map +1 -0
- package/dist/collection/lib/import-export/test-results-csv.js +65 -0
- package/dist/collection/lib/import-export/test-results-csv.js.map +1 -0
- package/dist/collection/lib/import-export/test-suite-exporter.js +15 -0
- package/dist/collection/lib/import-export/test-suite-exporter.js.map +1 -0
- package/dist/collection/lib/import-export/test-suite-importer.js +44 -0
- package/dist/collection/lib/import-export/test-suite-importer.js.map +1 -0
- package/dist/collection/lib/rate-limited-fetcher/rate-limited-fetcher.js +6 -6
- package/dist/collection/lib/rate-limited-fetcher/rate-limited-fetcher.js.map +1 -1
- package/dist/collection/lib/test-cases/test-case-factory.js +56 -0
- package/dist/collection/lib/test-cases/test-case-factory.js.map +1 -0
- package/dist/collection/lib/test-cases/test-case-mutations.js +16 -0
- package/dist/collection/lib/test-cases/test-case-mutations.js.map +1 -0
- package/dist/collection/lib/ui/button/button.css +113 -0
- package/dist/collection/lib/ui/button/button.js +21 -0
- package/dist/collection/lib/ui/button/button.js.map +1 -0
- package/dist/collection/lib/ui/button/index.js +2 -0
- package/dist/collection/lib/ui/button/index.js.map +1 -0
- package/dist/collection/lib/ui/icon-button/icon-button.css +77 -0
- package/dist/collection/lib/ui/icon-button/icon-button.js +19 -0
- package/dist/collection/lib/ui/icon-button/icon-button.js.map +1 -0
- package/dist/collection/lib/ui/icon-button/index.js +2 -0
- package/dist/collection/lib/ui/icon-button/index.js.map +1 -0
- package/dist/collection/services/adapters.js +2 -0
- package/dist/collection/services/adapters.js.map +1 -0
- package/dist/collection/services/models/gemini.js +17 -0
- package/dist/collection/services/models/gemini.js.map +1 -0
- package/dist/collection/styles/tokens.css +180 -0
- package/dist/collection/types/evaluation.js +2 -0
- package/dist/collection/types/evaluation.js.map +1 -0
- package/dist/collection/types/llm-test-runner.js +2 -0
- package/dist/collection/types/llm-test-runner.js.map +1 -0
- package/dist/components/app-chips.d.ts +11 -0
- package/dist/components/app-chips.js +2 -0
- package/dist/components/app-chips.js.map +1 -0
- package/dist/components/app-select.d.ts +11 -0
- package/dist/components/app-select.js +2 -0
- package/dist/components/app-select.js.map +1 -0
- package/dist/components/app-textarea.d.ts +11 -0
- package/dist/components/app-textarea.js +2 -0
- package/dist/components/app-textarea.js.map +1 -0
- package/dist/components/form-builder.d.ts +11 -0
- package/dist/components/form-builder.js +2 -0
- package/dist/components/form-builder.js.map +1 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.js +1 -13
- package/dist/components/index.js.map +1 -1
- package/dist/components/llm-test-runner.js +1 -8
- package/dist/components/llm-test-runner.js.map +1 -1
- package/dist/components/p--2rdv_J9.js +2 -0
- package/dist/components/p--2rdv_J9.js.map +1 -0
- package/dist/components/p-B7J48VNq.js +2 -0
- package/dist/components/p-B7J48VNq.js.map +1 -0
- package/dist/components/p-BCB1rjPS.js +7 -0
- package/dist/components/p-BCB1rjPS.js.map +1 -0
- package/dist/components/p-BQhb2H_a.js +2 -0
- package/dist/components/p-BQhb2H_a.js.map +1 -0
- package/dist/components/p-D9BrlHdP.js +297 -0
- package/dist/components/p-D9BrlHdP.js.map +1 -0
- package/dist/components/p-DtCkZ1g2.js +2 -0
- package/dist/components/p-DtCkZ1g2.js.map +1 -0
- package/dist/esm/app-chips_5.entry.js +153 -0
- package/dist/esm/app-chips_5.entry.js.map +1 -0
- package/dist/esm/app-globals-DbR5vV7d.js +32 -0
- package/dist/esm/app-globals-DbR5vV7d.js.map +1 -0
- package/dist/esm/index-Bvg6mh1M.js +25539 -0
- package/dist/esm/index-Bvg6mh1M.js.map +1 -0
- package/dist/esm/index-DxzhGhec.js +21450 -0
- package/dist/esm/index-DxzhGhec.js.map +1 -0
- package/dist/esm/index.js +4 -486
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/llm-testrunner.js +7 -5
- package/dist/esm/llm-testrunner.js.map +1 -1
- package/dist/esm/loader.js +6 -4
- package/dist/llm-testrunner/index.esm.js +1 -1
- package/dist/llm-testrunner/index.esm.js.map +1 -1
- package/dist/llm-testrunner/llm-testrunner.esm.js +1 -1
- package/dist/llm-testrunner/llm-testrunner.esm.js.map +1 -1
- package/dist/llm-testrunner/p-3f04b0fb.entry.js +2 -0
- package/dist/llm-testrunner/p-3f04b0fb.entry.js.map +1 -0
- package/dist/llm-testrunner/p-DFds8y01.js +7 -0
- package/dist/llm-testrunner/p-DFds8y01.js.map +1 -0
- package/dist/llm-testrunner/p-DxzhGhec.js +298 -0
- package/dist/llm-testrunner/p-DxzhGhec.js.map +1 -0
- package/dist/llm-testrunner/p-GQwFOmwJ.js +2 -0
- package/dist/llm-testrunner/p-GQwFOmwJ.js.map +1 -0
- package/dist/react/components.d.ts +32 -2
- package/dist/react/components.d.ts.map +1 -1
- package/dist/react/components.js +44 -2
- package/dist/types/components/llm-test-runner/header/llm-test-runner-header.d.ts +14 -0
- package/dist/types/components/llm-test-runner/llm-test-runner.d.ts +13 -29
- package/dist/types/components/llm-test-runner/llm-test-runner.import-export.test.d.ts +1 -0
- package/dist/types/components/llm-test-runner/test-cases/actions/row-actions.d.ts +8 -0
- package/dist/types/components/llm-test-runner/test-cases/evaluation/evaluation-summary.d.ts +7 -0
- package/dist/types/components/llm-test-runner/test-cases/llm-test-case-row.d.ts +25 -0
- package/dist/types/components/llm-test-runner/test-cases/llm-test-cases.d.ts +26 -0
- package/dist/types/components/llm-test-runner/test-cases/output/response-output.d.ts +6 -0
- package/dist/types/components.d.ts +199 -4
- package/dist/types/global/env.d.ts +2 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/lib/evaluation/constants.d.ts +11 -0
- package/dist/types/lib/evaluation/evaluation-engine.d.ts +0 -4
- package/dist/types/lib/evaluation/evaluation-service.d.ts +15 -0
- package/dist/types/lib/evaluation/evaluators/bleu/bleu-evaluator.d.ts +18 -0
- package/dist/types/lib/evaluation/evaluators/bleu/tests/bleu.test.d.ts +1 -0
- package/dist/types/lib/evaluation/evaluators/exact/exact.d.ts +2 -0
- package/dist/types/lib/evaluation/evaluators/rouge1-evaluator.d.ts +17 -0
- package/dist/types/lib/evaluation/evaluators/rougeL-evaluator.d.ts +2 -0
- package/dist/types/lib/evaluation/evaluators/rougeL-evaluator.test.d.ts +1 -0
- package/dist/types/lib/evaluation/evaluators/semantic/SemanticEvaluator.d.ts +6 -0
- package/dist/types/lib/evaluation/evaluators/semantic/evaluate-keywords.d.ts +7 -0
- package/dist/types/lib/evaluation/evaluators/semantic/index.d.ts +2 -0
- package/dist/types/lib/evaluation/evaluators/semantic/model-loader.d.ts +1 -0
- package/dist/types/lib/evaluation/evaluators/semantic/similarity-utils.d.ts +1 -0
- package/dist/types/lib/evaluation/evaluators/semantic/tests/evaluate-keywords.test.d.ts +1 -0
- package/dist/types/lib/evaluation/evaluators/semantic/text-utils.d.ts +1 -0
- package/dist/types/lib/evaluation/index.d.ts +2 -2
- package/dist/types/lib/evaluation/rouge1-evaluator.test.d.ts +1 -0
- package/dist/types/lib/evaluation/types.d.ts +19 -7
- package/dist/types/lib/file/file-download.d.ts +7 -0
- package/dist/types/lib/file/file-reader.d.ts +6 -0
- package/dist/types/lib/form/components/app-chips.d.ts +20 -0
- package/dist/types/lib/form/components/app-select.d.ts +7 -0
- package/dist/types/lib/form/components/app-textarea.d.ts +14 -0
- package/dist/types/lib/form/form-builder.d.ts +24 -0
- package/dist/types/lib/form/schema/base-input-field-config.d.ts +37 -0
- package/dist/types/lib/form/schema/form-control-config.d.ts +13 -0
- package/dist/types/lib/form/schema/index.d.ts +9 -0
- package/dist/types/lib/import-export/test-results-csv.d.ts +13 -0
- package/dist/types/lib/import-export/test-suite-exporter.d.ts +16 -0
- package/dist/types/lib/import-export/test-suite-importer.d.ts +12 -0
- package/dist/types/lib/rate-limited-fetcher/rate-limited-fetcher.d.ts +1 -1
- package/dist/types/lib/test-cases/test-case-factory.d.ts +12 -0
- package/dist/types/lib/test-cases/test-case-mutations.d.ts +9 -0
- package/dist/types/lib/ui/button/button.d.ts +13 -0
- package/dist/types/lib/ui/button/index.d.ts +2 -0
- package/dist/types/lib/ui/icon-button/icon-button.d.ts +11 -0
- package/dist/types/lib/ui/icon-button/index.d.ts +2 -0
- package/dist/types/services/adapters.d.ts +3 -0
- package/dist/types/services/models/gemini.d.ts +11 -0
- package/dist/types/stencil-public-runtime.d.ts +110 -6
- package/dist/types/types/evaluation.d.ts +9 -0
- package/dist/types/types/llm-test-runner.d.ts +22 -0
- package/package.json +30 -6
- package/dist/cjs/app-globals-CbbEbofA.js +0 -14
- package/dist/cjs/app-globals-CbbEbofA.js.map +0 -1
- package/dist/cjs/index-D-FySkoV.js +0 -1470
- package/dist/cjs/index-D-FySkoV.js.map +0 -1
- package/dist/cjs/llm-test-runner.cjs.entry.js +0 -9
- package/dist/cjs/llm-test-runner.entry.cjs.js.map +0 -1
- package/dist/components/p-CYUbsbxt.js +0 -1770
- package/dist/components/p-CYUbsbxt.js.map +0 -1
- package/dist/esm/app-globals-BOQOUavG.js +0 -12
- package/dist/esm/app-globals-BOQOUavG.js.map +0 -1
- package/dist/esm/index-cncubhtM.js +0 -1463
- package/dist/esm/index-cncubhtM.js.map +0 -1
- package/dist/esm/llm-test-runner.entry.js +0 -3
- package/dist/esm/llm-test-runner.entry.js.map +0 -1
- package/dist/llm-testrunner/llm-test-runner.entry.esm.js.map +0 -1
- package/dist/llm-testrunner/loader.esm.js.map +0 -1
- package/dist/llm-testrunner/p-BOQOUavG.js +0 -2
- package/dist/llm-testrunner/p-BOQOUavG.js.map +0 -1
- package/dist/llm-testrunner/p-cncubhtM.js +0 -3
- package/dist/llm-testrunner/p-cncubhtM.js.map +0 -1
- package/dist/llm-testrunner/p-f68fd660.entry.js +0 -2
- package/dist/llm-testrunner/p-f68fd660.entry.js.map +0 -1
|
@@ -1,12 +1,45 @@
|
|
|
1
|
+
import { performEvaluation } from "./evaluators/exact/exact";
|
|
2
|
+
import { EvaluationApproach } from "./constants";
|
|
3
|
+
import { performRouge1Evaluation } from "./evaluators/rouge1-evaluator";
|
|
4
|
+
import { performSemanticEvaluation } from "./evaluators/semantic/index";
|
|
5
|
+
import { performRougeLEvaluation } from "./evaluators/rougeL-evaluator";
|
|
6
|
+
import { performBleuEvaluation } from "./evaluators/bleu/bleu-evaluator";
|
|
1
7
|
export class LLMEvaluationEngine {
|
|
2
|
-
constructor() { }
|
|
3
8
|
async evaluateResponse(request, callback) {
|
|
4
9
|
try {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
const approach = request.evaluationParameters.approach;
|
|
11
|
+
switch (approach) {
|
|
12
|
+
case EvaluationApproach.BLEU: {
|
|
13
|
+
const bleuResult = performBleuEvaluation(request);
|
|
14
|
+
callback(bleuResult);
|
|
15
|
+
break;
|
|
16
|
+
}
|
|
17
|
+
case EvaluationApproach.EXACT: {
|
|
18
|
+
const exactResult = await performEvaluation(request);
|
|
19
|
+
callback(exactResult);
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
22
|
+
case EvaluationApproach.ROUGE_1: {
|
|
23
|
+
const rougeResult = await performRouge1Evaluation(request);
|
|
24
|
+
callback(rougeResult);
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
case EvaluationApproach.ROUGE_L: {
|
|
28
|
+
const rougeLResult = await performRougeLEvaluation(request);
|
|
29
|
+
callback(rougeLResult);
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
case EvaluationApproach.SEMANTIC: {
|
|
33
|
+
const semanticResult = await performSemanticEvaluation(request);
|
|
34
|
+
callback(semanticResult);
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
default: {
|
|
38
|
+
console.warn(`Unknown matching approach: ${request.evaluationParameters.approach}, falling back to exact matching`);
|
|
39
|
+
const fallbackResult = await performEvaluation(request);
|
|
40
|
+
callback(fallbackResult);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
10
43
|
}
|
|
11
44
|
catch (error) {
|
|
12
45
|
console.error('Evaluation failed:', error);
|
|
@@ -14,48 +47,15 @@ export class LLMEvaluationEngine {
|
|
|
14
47
|
testCaseId: request.testCaseId,
|
|
15
48
|
passed: false,
|
|
16
49
|
keywordMatches: [],
|
|
17
|
-
|
|
18
|
-
|
|
50
|
+
timestamp: new Date().toISOString(),
|
|
51
|
+
evaluationParameters: request.evaluationParameters,
|
|
52
|
+
evaluationApproachResult: {
|
|
53
|
+
score: 0,
|
|
54
|
+
approachUsed: EvaluationApproach.EXACT,
|
|
55
|
+
},
|
|
19
56
|
};
|
|
20
57
|
callback(errorResult);
|
|
21
58
|
}
|
|
22
59
|
}
|
|
23
|
-
async performEvaluation(request) {
|
|
24
|
-
const { testCaseId, expectedKeywords, expectedSourceLinks, actualResponse } = request;
|
|
25
|
-
const keywordMatches = this.evaluateKeywords(expectedKeywords, actualResponse);
|
|
26
|
-
const sourceLinkMatches = this.evaluateSourceLinks(expectedSourceLinks, actualResponse);
|
|
27
|
-
// Test passes only if ALL expected keywords and source links are found
|
|
28
|
-
const totalItems = keywordMatches.length + sourceLinkMatches.length;
|
|
29
|
-
const foundItems = keywordMatches.filter(m => m.found).length + sourceLinkMatches.filter(m => m.found).length;
|
|
30
|
-
const passed = foundItems === totalItems;
|
|
31
|
-
return {
|
|
32
|
-
testCaseId,
|
|
33
|
-
passed,
|
|
34
|
-
keywordMatches,
|
|
35
|
-
sourceLinkMatches,
|
|
36
|
-
timestamp: new Date().toISOString()
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
evaluateKeywords(expectedKeywords, actualResponse) {
|
|
40
|
-
// Case-insensitive keyword matching
|
|
41
|
-
const response = actualResponse.toLowerCase();
|
|
42
|
-
return expectedKeywords.map(keyword => {
|
|
43
|
-
const keywordToMatch = keyword.toLowerCase();
|
|
44
|
-
const found = response.includes(keywordToMatch);
|
|
45
|
-
return {
|
|
46
|
-
keyword,
|
|
47
|
-
found
|
|
48
|
-
};
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
evaluateSourceLinks(expectedSourceLinks, actualResponse) {
|
|
52
|
-
return expectedSourceLinks.map(link => {
|
|
53
|
-
const found = actualResponse.includes(link);
|
|
54
|
-
return {
|
|
55
|
-
link,
|
|
56
|
-
found
|
|
57
|
-
};
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
60
|
}
|
|
61
61
|
//# sourceMappingURL=evaluation-engine.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"evaluation-engine.js","sourceRoot":"","sources":["../../../src/lib/evaluation/evaluation-engine.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"evaluation-engine.js","sourceRoot":"","sources":["../../../src/lib/evaluation/evaluation-engine.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AACxE,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AACxE,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AAEzE,MAAM,OAAO,mBAAmB;IAC9B,KAAK,CAAC,gBAAgB,CACpB,OAA0B,EAC1B,QAA4B;QAE5B,IAAI,CAAC;YACH,MAAM,QAAQ,GACZ,OAAO,CAAC,oBAAoB,CAAC,QAAQ,CAAC;YACxC,QAAQ,QAAQ,EAAE,CAAC;gBACjB,KAAK,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC;oBAC7B,MAAM,UAAU,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAC;oBAClD,QAAQ,CAAC,UAAU,CAAC,CAAC;oBACrB,MAAM;gBACR,CAAC;gBAED,KAAK,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC;oBAC9B,MAAM,WAAW,GAAG,MAAM,iBAAiB,CAAC,OAAO,CAAC,CAAC;oBACrD,QAAQ,CAAC,WAAW,CAAC,CAAC;oBACtB,MAAM;gBACR,CAAC;gBAED,KAAK,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC;oBAChC,MAAM,WAAW,GAAG,MAAM,uBAAuB,CAAC,OAAO,CAAC,CAAC;oBAC3D,QAAQ,CAAC,WAAW,CAAC,CAAC;oBACtB,MAAM;gBACR,CAAC;gBAED,KAAK,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC;oBAChC,MAAM,YAAY,GAAG,MAAM,uBAAuB,CAAC,OAAO,CAAC,CAAC;oBAC5D,QAAQ,CAAC,YAAY,CAAC,CAAC;oBACvB,MAAM;gBACR,CAAC;gBAED,KAAK,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAC;oBACjC,MAAM,cAAc,GAAG,MAAM,yBAAyB,CAAC,OAAO,CAAC,CAAC;oBAChE,QAAQ,CAAC,cAAc,CAAC,CAAC;oBACzB,MAAM;gBACR,CAAC;gBAED,OAAO,CAAC,CAAC,CAAC;oBACR,OAAO,CAAC,IAAI,CACV,8BAA8B,OAAO,CAAC,oBAAoB,CAAC,QAAQ,kCAAkC,CACtG,CAAC;oBACF,MAAM,cAAc,GAAG,MAAM,iBAAiB,CAAC,OAAO,CAAC,CAAC;oBACxD,QAAQ,CAAC,cAAc,CAAC,CAAC;gBAC3B,CAAC;YACH,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,oBAAoB,EAAE,KAAK,CAAC,CAAC;YAE3C,MAAM,WAAW,GAAqB;gBACpC,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,MAAM,EAAE,KAAK;gBACb,cAAc,EAAE,EAAE;gBAClB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gBACnC,oBAAoB,EAAE,OAAO,CAAC,oBAAoB;gBAClD,wBAAwB,EAAE;oBACxB,KAAK,EAAE,CAAC;oBACR,YAAY,EAAE,kBAAkB,CAAC,KAAK;iBACvC;aACF,CAAC;YAEF,QAAQ,CAAC,WAAW,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;CACF","sourcesContent":["import {\n EvaluationRequest,\n EvaluationResult,\n EvaluationCallback,\n} from './types';\nimport { performEvaluation } from './evaluators/exact/exact';\nimport { EvaluationApproach } from './constants';\nimport { performRouge1Evaluation } from './evaluators/rouge1-evaluator';\nimport { performSemanticEvaluation } from './evaluators/semantic/index';\nimport { performRougeLEvaluation } from './evaluators/rougeL-evaluator';\nimport { performBleuEvaluation } from './evaluators/bleu/bleu-evaluator';\n\nexport class LLMEvaluationEngine {\n async evaluateResponse(\n request: EvaluationRequest,\n callback: EvaluationCallback,\n ): Promise<void> {\n try {\n const approach: EvaluationApproach =\n request.evaluationParameters.approach;\n switch (approach) {\n case EvaluationApproach.BLEU: {\n const bleuResult = performBleuEvaluation(request);\n callback(bleuResult);\n break;\n }\n\n case EvaluationApproach.EXACT: {\n const exactResult = await performEvaluation(request);\n callback(exactResult);\n break;\n }\n\n case EvaluationApproach.ROUGE_1: {\n const rougeResult = await performRouge1Evaluation(request);\n callback(rougeResult);\n break;\n }\n\n case EvaluationApproach.ROUGE_L: {\n const rougeLResult = await performRougeLEvaluation(request);\n callback(rougeLResult);\n break;\n }\n\n case EvaluationApproach.SEMANTIC: {\n const semanticResult = await performSemanticEvaluation(request);\n callback(semanticResult);\n break;\n }\n\n default: {\n console.warn(\n `Unknown matching approach: ${request.evaluationParameters.approach}, falling back to exact matching`,\n );\n const fallbackResult = await performEvaluation(request);\n callback(fallbackResult);\n }\n }\n } catch (error) {\n console.error('Evaluation failed:', error);\n\n const errorResult: EvaluationResult = {\n testCaseId: request.testCaseId,\n passed: false,\n keywordMatches: [],\n timestamp: new Date().toISOString(),\n evaluationParameters: request.evaluationParameters,\n evaluationApproachResult: {\n score: 0,\n approachUsed: EvaluationApproach.EXACT,\n },\n };\n\n callback(errorResult);\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { LLMEvaluationEngine } from "./evaluation-engine";
|
|
2
|
+
/**
|
|
3
|
+
* Service for evaluating test case responses
|
|
4
|
+
*/
|
|
5
|
+
export class EvaluationService {
|
|
6
|
+
engine;
|
|
7
|
+
constructor() {
|
|
8
|
+
this.engine = new LLMEvaluationEngine();
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Evaluates a test case response
|
|
12
|
+
* @param testCase - The test case to evaluate
|
|
13
|
+
* @param onResult - Callback to handle the evaluation result
|
|
14
|
+
*/
|
|
15
|
+
async evaluateTestCase(testCase, onResult) {
|
|
16
|
+
if (!testCase.output) {
|
|
17
|
+
console.warn('⚠️ No output to evaluate for test case:', testCase.id);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const evaluationRequest = {
|
|
21
|
+
testCaseId: testCase.id,
|
|
22
|
+
question: testCase.question,
|
|
23
|
+
expectedOutcome: testCase.expectedOutcome,
|
|
24
|
+
actualResponse: testCase.output,
|
|
25
|
+
evaluationParameters: testCase.evaluationParameters,
|
|
26
|
+
};
|
|
27
|
+
await this.engine.evaluateResponse(evaluationRequest, (result) => {
|
|
28
|
+
console.log('📊 Evaluation result received:', result);
|
|
29
|
+
onResult(result);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=evaluation-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evaluation-service.js","sourceRoot":"","sources":["../../../src/lib/evaluation/evaluation-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAI1D;;GAEG;AACH,MAAM,OAAO,iBAAiB;IACpB,MAAM,CAAsB;IAEpC;QACE,IAAI,CAAC,MAAM,GAAG,IAAI,mBAAmB,EAAE,CAAC;IAC1C,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,gBAAgB,CACpB,QAAkB,EAClB,QAA4C;QAE5C,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;YACrB,OAAO,CAAC,IAAI,CAAC,yCAAyC,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;YACrE,OAAO;QACT,CAAC;QAED,MAAM,iBAAiB,GAAsB;YAC3C,UAAU,EAAE,QAAQ,CAAC,EAAE;YACvB,QAAQ,EAAE,QAAQ,CAAC,QAAQ;YAC3B,eAAe,EAAE,QAAQ,CAAC,eAAe;YACzC,cAAc,EAAE,QAAQ,CAAC,MAAM;YAC/B,oBAAoB,EAAE,QAAQ,CAAC,oBAAoB;SACpD,CAAC;QAEF,MAAM,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAChC,iBAAiB,EACjB,CAAC,MAAwB,EAAE,EAAE;YAC3B,OAAO,CAAC,GAAG,CAAC,gCAAgC,EAAE,MAAM,CAAC,CAAC;YACtD,QAAQ,CAAC,MAAM,CAAC,CAAC;QACnB,CAAC,CACF,CAAC;IACJ,CAAC;CACF","sourcesContent":["import { LLMEvaluationEngine } from './evaluation-engine';\nimport { EvaluationRequest, EvaluationResult } from './types';\nimport { TestCase } from '../../types/llm-test-runner';\n\n/**\n * Service for evaluating test case responses\n */\nexport class EvaluationService {\n private engine: LLMEvaluationEngine;\n\n constructor() {\n this.engine = new LLMEvaluationEngine();\n }\n\n /**\n * Evaluates a test case response\n * @param testCase - The test case to evaluate\n * @param onResult - Callback to handle the evaluation result\n */\n async evaluateTestCase(\n testCase: TestCase,\n onResult: (result: EvaluationResult) => void,\n ): Promise<void> {\n if (!testCase.output) {\n console.warn('⚠️ No output to evaluate for test case:', testCase.id);\n return;\n }\n\n const evaluationRequest: EvaluationRequest = {\n testCaseId: testCase.id,\n question: testCase.question,\n expectedOutcome: testCase.expectedOutcome,\n actualResponse: testCase.output,\n evaluationParameters: testCase.evaluationParameters,\n };\n\n await this.engine.evaluateResponse(\n evaluationRequest,\n (result: EvaluationResult) => {\n console.log('📊 Evaluation result received:', result);\n onResult(result);\n },\n );\n }\n}\n"]}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { bleu } from "bleu-score";
|
|
2
|
+
import { DEFAULT_BLEU_PASS_SCORE, EvaluationApproach } from "../../constants";
|
|
3
|
+
/**
|
|
4
|
+
* Normalizes text by converting to lowercase and normalizing whitespace.
|
|
5
|
+
* Also removes punctuation that would interfere with n-gram matching.
|
|
6
|
+
*
|
|
7
|
+
* @param {string} text - The text to normalize
|
|
8
|
+
* @returns {string} The normalized text
|
|
9
|
+
*/
|
|
10
|
+
function normalizeText(text) {
|
|
11
|
+
return text
|
|
12
|
+
.trim()
|
|
13
|
+
.toLowerCase()
|
|
14
|
+
.replace(/[.,!?;:()]/g, ' ') // Replace punctuation with spaces
|
|
15
|
+
.replace(/\s+/g, ' '); // Replace multiple whitespace with single space
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Evaluates a single keyword against the candidate text using BLEU score.
|
|
19
|
+
*
|
|
20
|
+
* @param {string} keyword - The expected keyword (reference text)
|
|
21
|
+
* @param {string} candidate - The actual response text (candidate text)
|
|
22
|
+
* @param {number} bleuThreshold - The minimum BLEU score required to pass
|
|
23
|
+
* @returns {KeywordMatch} The evaluation result for this keyword
|
|
24
|
+
*/
|
|
25
|
+
function evaluateKeyword(keyword, candidate, bleuThreshold) {
|
|
26
|
+
let bleuScore = 0;
|
|
27
|
+
try {
|
|
28
|
+
const normalizedKeyword = normalizeText(keyword);
|
|
29
|
+
const normalizedCandidate = normalizeText(candidate);
|
|
30
|
+
if (normalizedKeyword.length > 0 && normalizedCandidate.length > 0) {
|
|
31
|
+
// BLEU function signature: bleu(reference, candidate, maxN)
|
|
32
|
+
// reference: the expected keyword (ground truth)
|
|
33
|
+
// candidate: the actual response text
|
|
34
|
+
// maxN: maximum n-gram order (typically 4 for standard BLEU)
|
|
35
|
+
// Adjust maxN based on keyword length - it should not exceed the number of words in the keyword
|
|
36
|
+
const keywordTokens = normalizedKeyword.split(/\s+/).length;
|
|
37
|
+
const maxN = Math.min(4, Math.max(1, keywordTokens)); // Use up to 4-grams, but respect keyword length
|
|
38
|
+
const bleuResult = bleu(normalizedKeyword, normalizedCandidate, maxN);
|
|
39
|
+
bleuScore = isNaN(bleuResult) ? 0 : bleuResult;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
console.warn(`BLEU not computed for keyword "${keyword}": Keyword or Candidate is missing.`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
console.error(`BLEU computation failed for keyword "${keyword}":`, err);
|
|
47
|
+
}
|
|
48
|
+
const keywordPassed = bleuScore >= bleuThreshold;
|
|
49
|
+
const keywordApproachResult = {
|
|
50
|
+
score: bleuScore,
|
|
51
|
+
approachUsed: EvaluationApproach.BLEU,
|
|
52
|
+
};
|
|
53
|
+
return {
|
|
54
|
+
keyword: keyword,
|
|
55
|
+
found: keywordPassed,
|
|
56
|
+
evaluationApproachResult: keywordApproachResult,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Computes the BLEU score for keywords against the candidate text.
|
|
61
|
+
*
|
|
62
|
+
* BLEU measures the precision of n-grams (typically 1-4 grams) between the candidate
|
|
63
|
+
* and reference text. A score of 1.0 indicates perfect match.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* const match = performBleuEvaluation({
|
|
67
|
+
* testCaseId: 'test-1',
|
|
68
|
+
* question: 'What is the capital?',
|
|
69
|
+
* expectedKeywords: ['Paris'],
|
|
70
|
+
* actualResponse: 'The capital is Paris.',
|
|
71
|
+
* evaluationParameters: { approach: 'bleu', threshold: 0.7 }
|
|
72
|
+
* });
|
|
73
|
+
* // Returns evaluation result with BLEU scores for each keyword
|
|
74
|
+
*/
|
|
75
|
+
export function performBleuEvaluation(request) {
|
|
76
|
+
const { testCaseId, actualResponse, expectedOutcome, evaluationParameters } = request;
|
|
77
|
+
// Split expectedOutcome by newlines, commas, and periods to create keywords array
|
|
78
|
+
let expectedKeywords = expectedOutcome
|
|
79
|
+
? expectedOutcome
|
|
80
|
+
.split(/[\n,.]+/)
|
|
81
|
+
.map(k => k.trim())
|
|
82
|
+
.filter(k => k.length > 0)
|
|
83
|
+
: [];
|
|
84
|
+
// If no keywords after filtering (e.g., whitespace-only input), treat the original input as a single keyword
|
|
85
|
+
if (expectedKeywords.length === 0 && expectedOutcome) {
|
|
86
|
+
expectedKeywords = [expectedOutcome];
|
|
87
|
+
}
|
|
88
|
+
const candidate = (actualResponse || '').trim();
|
|
89
|
+
const bleuThreshold = evaluationParameters.threshold ?? DEFAULT_BLEU_PASS_SCORE;
|
|
90
|
+
let keywordsPassed = 0;
|
|
91
|
+
const totalKeywords = expectedKeywords.length;
|
|
92
|
+
const keywordMatches = expectedKeywords.map(keyword => {
|
|
93
|
+
const match = evaluateKeyword(keyword, candidate, bleuThreshold);
|
|
94
|
+
if (match.found) {
|
|
95
|
+
keywordsPassed++;
|
|
96
|
+
}
|
|
97
|
+
return match;
|
|
98
|
+
});
|
|
99
|
+
const overallPassed = keywordsPassed === totalKeywords;
|
|
100
|
+
const overallApproachResult = {
|
|
101
|
+
score: totalKeywords > 0 ? keywordsPassed / totalKeywords : 1,
|
|
102
|
+
approachUsed: EvaluationApproach.BLEU,
|
|
103
|
+
};
|
|
104
|
+
return {
|
|
105
|
+
testCaseId: testCaseId,
|
|
106
|
+
passed: overallPassed,
|
|
107
|
+
keywordMatches: keywordMatches,
|
|
108
|
+
timestamp: new Date().toISOString(),
|
|
109
|
+
evaluationParameters: {
|
|
110
|
+
...evaluationParameters,
|
|
111
|
+
threshold: bleuThreshold,
|
|
112
|
+
},
|
|
113
|
+
evaluationApproachResult: overallApproachResult,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=bleu-evaluator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bleu-evaluator.js","sourceRoot":"","sources":["../../../../../src/lib/evaluation/evaluators/bleu/bleu-evaluator.ts"],"names":[],"mappings":"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","sourcesContent":["import { bleu } from 'bleu-score';\nimport { EvaluationApproachResult } from '../../../../types/evaluation';\nimport { EvaluationRequest, EvaluationResult, KeywordMatch } from '../../types';\nimport { DEFAULT_BLEU_PASS_SCORE, EvaluationApproach } from '../../constants';\n\n/**\n * Normalizes text by converting to lowercase and normalizing whitespace.\n * Also removes punctuation that would interfere with n-gram matching.\n *\n * @param {string} text - The text to normalize\n * @returns {string} The normalized text\n */\nfunction normalizeText(text: string): string {\n return text\n .trim()\n .toLowerCase()\n .replace(/[.,!?;:()]/g, ' ') // Replace punctuation with spaces\n .replace(/\\s+/g, ' '); // Replace multiple whitespace with single space\n}\n\n/**\n * Evaluates a single keyword against the candidate text using BLEU score.\n *\n * @param {string} keyword - The expected keyword (reference text)\n * @param {string} candidate - The actual response text (candidate text)\n * @param {number} bleuThreshold - The minimum BLEU score required to pass\n * @returns {KeywordMatch} The evaluation result for this keyword\n */\nfunction evaluateKeyword(\n keyword: string,\n candidate: string,\n bleuThreshold: number,\n): KeywordMatch {\n let bleuScore = 0;\n\n try {\n const normalizedKeyword = normalizeText(keyword);\n const normalizedCandidate = normalizeText(candidate);\n\n if (normalizedKeyword.length > 0 && normalizedCandidate.length > 0) {\n // BLEU function signature: bleu(reference, candidate, maxN)\n // reference: the expected keyword (ground truth)\n // candidate: the actual response text\n // maxN: maximum n-gram order (typically 4 for standard BLEU)\n // Adjust maxN based on keyword length - it should not exceed the number of words in the keyword\n const keywordTokens = normalizedKeyword.split(/\\s+/).length;\n const maxN = Math.min(4, Math.max(1, keywordTokens)); // Use up to 4-grams, but respect keyword length\n const bleuResult = bleu(normalizedKeyword, normalizedCandidate, maxN);\n bleuScore = isNaN(bleuResult) ? 0 : bleuResult;\n } else {\n console.warn(\n `BLEU not computed for keyword \"${keyword}\": Keyword or Candidate is missing.`,\n );\n }\n } catch (err) {\n console.error(`BLEU computation failed for keyword \"${keyword}\":`, err);\n }\n\n const keywordPassed = bleuScore >= bleuThreshold;\n\n const keywordApproachResult: EvaluationApproachResult = {\n score: bleuScore,\n approachUsed: EvaluationApproach.BLEU,\n };\n\n return {\n keyword: keyword,\n found: keywordPassed,\n evaluationApproachResult: keywordApproachResult,\n };\n}\n\n/**\n * Computes the BLEU score for keywords against the candidate text.\n *\n * BLEU measures the precision of n-grams (typically 1-4 grams) between the candidate\n * and reference text. A score of 1.0 indicates perfect match.\n *\n * @example\n * const match = performBleuEvaluation({\n * testCaseId: 'test-1',\n * question: 'What is the capital?',\n * expectedKeywords: ['Paris'],\n * actualResponse: 'The capital is Paris.',\n * evaluationParameters: { approach: 'bleu', threshold: 0.7 }\n * });\n * // Returns evaluation result with BLEU scores for each keyword\n */\n\nexport function performBleuEvaluation(\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 bleuThreshold =\n evaluationParameters.threshold ?? DEFAULT_BLEU_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, bleuThreshold);\n\n if (match.found) {\n keywordsPassed++;\n }\n\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.BLEU,\n };\n\n return {\n testCaseId: testCaseId,\n passed: overallPassed,\n keywordMatches: keywordMatches,\n timestamp: new Date().toISOString(),\n evaluationParameters: {\n ...evaluationParameters,\n threshold: bleuThreshold,\n },\n evaluationApproachResult: overallApproachResult,\n };\n}\n"]}
|