promptfoo 0.72.1 → 0.73.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/dist/package.json +14 -8
- package/dist/src/accounts.d.ts.map +1 -1
- package/dist/src/accounts.js +2 -1
- package/dist/src/accounts.js.map +1 -1
- package/dist/src/assertions/validateAssertions.d.ts +1 -1
- package/dist/src/assertions/validateAssertions.d.ts.map +1 -1
- package/dist/src/assertions/validateAssertions.js +7 -7
- package/dist/src/assertions/validateAssertions.js.map +1 -1
- package/dist/src/assertions.d.ts.map +1 -1
- package/dist/src/assertions.js +2 -1
- package/dist/src/assertions.js.map +1 -1
- package/dist/src/cache.js +2 -2
- package/dist/src/cache.js.map +1 -1
- package/dist/src/commands/eval.d.ts.map +1 -1
- package/dist/src/commands/eval.js +10 -0
- package/dist/src/commands/eval.js.map +1 -1
- package/dist/src/commands/export.d.ts.map +1 -1
- package/dist/src/commands/export.js +15 -14
- package/dist/src/commands/export.js.map +1 -1
- package/dist/src/commands/generate/redteam.d.ts +1 -46
- package/dist/src/commands/generate/redteam.d.ts.map +1 -1
- package/dist/src/commands/generate/redteam.js +70 -26
- package/dist/src/commands/generate/redteam.js.map +1 -1
- package/dist/src/commands/import.d.ts.map +1 -1
- package/dist/src/commands/import.js +2 -1
- package/dist/src/commands/import.js.map +1 -1
- package/dist/src/commands/init.d.ts.map +1 -1
- package/dist/src/commands/init.js +12 -0
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/redteam.d.ts.map +1 -1
- package/dist/src/commands/redteam.js +72 -18
- package/dist/src/commands/redteam.js.map +1 -1
- package/dist/src/commands/view.d.ts.map +1 -1
- package/dist/src/commands/view.js +2 -1
- package/dist/src/commands/view.js.map +1 -1
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +2 -20
- package/dist/src/config.js.map +1 -1
- package/dist/src/configTypes.d.ts +7 -0
- package/dist/src/configTypes.d.ts.map +1 -0
- package/dist/src/configTypes.js +3 -0
- package/dist/src/configTypes.js.map +1 -0
- package/dist/src/database/index.d.ts +4 -0
- package/dist/src/database/index.d.ts.map +1 -0
- package/dist/src/database/index.js +50 -0
- package/dist/src/database/index.js.map +1 -0
- package/dist/src/{database.d.ts → database/operations.d.ts} +173 -115
- package/dist/src/{database.d.ts.map → database/operations.d.ts.map} +1 -1
- package/dist/src/{database.js → database/operations.js} +1 -25
- package/dist/src/database/operations.js.map +1 -0
- package/dist/src/evaluator.js +2 -2
- package/dist/src/evaluator.js.map +1 -1
- package/dist/src/globalConfig.d.ts +1 -7
- package/dist/src/globalConfig.d.ts.map +1 -1
- package/dist/src/globalConfig.js +3 -3
- package/dist/src/globalConfig.js.map +1 -1
- package/dist/src/matchers.d.ts.map +1 -1
- package/dist/src/matchers.js +6 -2
- package/dist/src/matchers.js.map +1 -1
- package/dist/src/prompts/grading.js +2 -2
- package/dist/src/prompts/processors/python.js +4 -4
- package/dist/src/prompts/processors/python.js.map +1 -1
- package/dist/src/providers/anthropic.d.ts +1 -1
- package/dist/src/providers/anthropic.d.ts.map +1 -1
- package/dist/src/providers/bam.d.ts +1 -1
- package/dist/src/providers/bam.d.ts.map +1 -1
- package/dist/src/providers/bedrock.d.ts +1 -1
- package/dist/src/providers/bedrock.d.ts.map +1 -1
- package/dist/src/providers/cohere.d.ts.map +1 -1
- package/dist/src/providers/cohere.js +4 -1
- package/dist/src/providers/cohere.js.map +1 -1
- package/dist/src/providers/http.d.ts +1 -1
- package/dist/src/providers/http.d.ts.map +1 -1
- package/dist/src/providers/http.js +3 -3
- package/dist/src/providers/http.js.map +1 -1
- package/dist/src/providers/llama.d.ts +1 -1
- package/dist/src/providers/llama.d.ts.map +1 -1
- package/dist/src/providers/localai.d.ts +1 -1
- package/dist/src/providers/localai.d.ts.map +1 -1
- package/dist/src/providers/ollama.d.ts +1 -1
- package/dist/src/providers/ollama.d.ts.map +1 -1
- package/dist/src/providers/openai.d.ts +1 -1
- package/dist/src/providers/openai.d.ts.map +1 -1
- package/dist/src/providers/openai.js +48 -50
- package/dist/src/providers/openai.js.map +1 -1
- package/dist/src/providers/palm.d.ts +1 -1
- package/dist/src/providers/palm.d.ts.map +1 -1
- package/dist/src/providers/pythonCompletion.d.ts.map +1 -1
- package/dist/src/providers/pythonCompletion.js +6 -5
- package/dist/src/providers/pythonCompletion.js.map +1 -1
- package/dist/src/providers/replicate.js +2 -2
- package/dist/src/providers/replicate.js.map +1 -1
- package/dist/src/providers/scriptCompletion.js +3 -3
- package/dist/src/providers/scriptCompletion.js.map +1 -1
- package/dist/src/providers/vertex.d.ts +1 -1
- package/dist/src/providers/vertex.d.ts.map +1 -1
- package/dist/src/providers/vertex.js +23 -10
- package/dist/src/providers/vertex.js.map +1 -1
- package/dist/src/providers/vertexUtil.d.ts +2 -1
- package/dist/src/providers/vertexUtil.d.ts.map +1 -1
- package/dist/src/providers/vertexUtil.js.map +1 -1
- package/dist/src/providers/webhook.d.ts +1 -1
- package/dist/src/providers/webhook.d.ts.map +1 -1
- package/dist/src/providers.d.ts.map +1 -1
- package/dist/src/providers.js +5 -2
- package/dist/src/providers.js.map +1 -1
- package/dist/src/python/pythonUtils.d.ts +4 -0
- package/dist/src/python/pythonUtils.d.ts.map +1 -0
- package/dist/src/python/pythonUtils.js +51 -0
- package/dist/src/python/pythonUtils.js.map +1 -0
- package/dist/src/python/wrapper.d.ts +0 -10
- package/dist/src/python/wrapper.d.ts.map +1 -1
- package/dist/src/python/wrapper.js +2 -49
- package/dist/src/python/wrapper.js.map +1 -1
- package/dist/src/redteam/constants.d.ts +60 -3
- package/dist/src/redteam/constants.d.ts.map +1 -1
- package/dist/src/redteam/constants.js +71 -10
- package/dist/src/redteam/constants.js.map +1 -1
- package/dist/src/redteam/index.d.ts +15 -4
- package/dist/src/redteam/index.d.ts.map +1 -1
- package/dist/src/redteam/index.js +110 -33
- package/dist/src/redteam/index.js.map +1 -1
- package/dist/src/redteam/plugins/base.d.ts +10 -13
- package/dist/src/redteam/plugins/base.d.ts.map +1 -1
- package/dist/src/redteam/plugins/base.js +36 -20
- package/dist/src/redteam/plugins/base.js.map +1 -1
- package/dist/src/redteam/plugins/debugInterface.d.ts +7 -0
- package/dist/src/redteam/plugins/debugInterface.d.ts.map +1 -0
- package/dist/src/redteam/plugins/debugInterface.js +49 -0
- package/dist/src/redteam/plugins/debugInterface.js.map +1 -0
- package/dist/src/redteam/plugins/harmful.d.ts +1 -54
- package/dist/src/redteam/plugins/harmful.d.ts.map +1 -1
- package/dist/src/redteam/plugins/harmful.js +8 -51
- package/dist/src/redteam/plugins/harmful.js.map +1 -1
- package/dist/src/redteam/plugins/pii.d.ts +9 -10
- package/dist/src/redteam/plugins/pii.d.ts.map +1 -1
- package/dist/src/redteam/plugins/pii.js +33 -53
- package/dist/src/redteam/plugins/pii.js.map +1 -1
- package/dist/src/redteam/plugins/rbac.d.ts +7 -0
- package/dist/src/redteam/plugins/rbac.d.ts.map +1 -0
- package/dist/src/redteam/plugins/rbac.js +49 -0
- package/dist/src/redteam/plugins/rbac.js.map +1 -0
- package/dist/src/redteam/plugins/shellInjection.d.ts +7 -0
- package/dist/src/redteam/plugins/shellInjection.d.ts.map +1 -0
- package/dist/src/redteam/plugins/shellInjection.js +54 -0
- package/dist/src/redteam/plugins/shellInjection.js.map +1 -0
- package/dist/src/redteam/plugins/sqlInjection.d.ts +7 -0
- package/dist/src/redteam/plugins/sqlInjection.d.ts.map +1 -0
- package/dist/src/redteam/plugins/sqlInjection.js +44 -0
- package/dist/src/redteam/plugins/sqlInjection.js.map +1 -0
- package/dist/src/redteam/providers/iterative.d.ts.map +1 -1
- package/dist/src/redteam/providers/iterative.js +2 -1
- package/dist/src/redteam/providers/iterative.js.map +1 -1
- package/dist/src/redteam/strategies/injections.d.ts.map +1 -0
- package/dist/src/redteam/{methods → strategies}/injections.js +0 -12
- package/dist/src/redteam/strategies/injections.js.map +1 -0
- package/dist/src/redteam/strategies/iterative.d.ts.map +1 -0
- package/dist/src/redteam/strategies/iterative.js.map +1 -0
- package/dist/src/redteam/types.d.ts +196 -0
- package/dist/src/redteam/types.d.ts.map +1 -0
- package/dist/src/redteam/types.js +124 -0
- package/dist/src/redteam/types.js.map +1 -0
- package/dist/src/redteam/util.d.ts +22 -0
- package/dist/src/redteam/util.d.ts.map +1 -0
- package/dist/src/redteam/util.js +56 -0
- package/dist/src/redteam/util.js.map +1 -0
- package/dist/src/testCases.d.ts +1 -2
- package/dist/src/testCases.d.ts.map +1 -1
- package/dist/src/testCases.js.map +1 -1
- package/dist/src/types/databaseTypes.d.ts +17 -0
- package/dist/src/types/databaseTypes.d.ts.map +1 -0
- package/dist/src/types/databaseTypes.js +3 -0
- package/dist/src/types/databaseTypes.js.map +1 -0
- package/dist/src/{types.d.ts → types/index.d.ts} +4296 -1425
- package/dist/src/{types.d.ts.map → types/index.d.ts.map} +1 -1
- package/dist/src/{types.js → types/index.js} +38 -9
- package/dist/src/types/index.js.map +1 -0
- package/dist/src/util/config.d.ts +3 -0
- package/dist/src/util/config.d.ts.map +1 -0
- package/dist/src/util/config.js +42 -0
- package/dist/src/util/config.js.map +1 -0
- package/dist/src/util/index.d.ts +41 -25
- package/dist/src/util/index.d.ts.map +1 -1
- package/dist/src/util/index.js +81 -119
- package/dist/src/util/index.js.map +1 -1
- package/dist/src/util/json.d.ts +3 -0
- package/dist/src/util/json.d.ts.map +1 -0
- package/dist/src/util/json.js +27 -0
- package/dist/src/util/json.js.map +1 -0
- package/dist/src/web/nextui/404/index.html +1 -1
- package/dist/src/web/nextui/404.html +1 -1
- package/dist/src/web/nextui/_next/static/chunks/155-4e319e68476266ee.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/163-93bab94bc04ae71b.js +6 -0
- package/dist/src/web/nextui/_next/static/chunks/{258-4acb452fe85cff6f.js → 258-b6257e5de9d0e2c7.js} +1 -1
- package/dist/src/web/nextui/_next/static/chunks/281-64d9f1415a301ee5.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/304-cf667fe8f06238b4.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/322-b47b6cc3f5b5fdb8.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/325-0d36870ade5e5263.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/339-79124d204fa988a3.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/378-c135e497df1864cb.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/476-0afb5eb266c57b7f.js +17 -0
- package/dist/src/web/nextui/_next/static/chunks/493-ebd12f00541c4969.js +2 -0
- package/dist/src/web/nextui/_next/static/chunks/497-e280a5610a3d7d42.js +32 -0
- package/dist/src/web/nextui/_next/static/chunks/515-e06d044f12d8a1bd.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/69-5e4d5e60859a86d2.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/737-32d5472455807d7a.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/903-7cc440d9f9e9f95d.js +97 -0
- package/dist/src/web/nextui/_next/static/chunks/905-78cd666f27891ee6.js +28 -0
- package/dist/src/web/nextui/_next/static/chunks/916-b92fea2ab6136411.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/969-6ab6c8f4158da970.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/app/auth/login/page-7247b17e8f179a46.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/app/auth/signup/page-8560afaf494f9882.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/app/datasets/page-6e6ec3c778ccd110.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/app/eval/[id]/not-found-fe10d5df88bc44ef.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/app/eval/[id]/{page-0a39d3450aa09dda.js → page-057e5eb83e0f614b.js} +1 -1
- package/dist/src/web/nextui/_next/static/chunks/app/eval/page-88fa70fc5bf755bc.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/app/layout-15abf0d1049cb47c.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/app/{page-5650318e57510b6c.js → page-122e9cfa52eb218a.js} +1 -1
- package/dist/src/web/nextui/_next/static/chunks/app/progress/page-a62ca531a4bb4149.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/app/prompts/page-10bb2e571670139c.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/app/report/page-a8b83960f08e2bb2.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/app/setup/page-6ea3e180c3116fb2.js +1 -0
- package/dist/src/web/nextui/_next/static/chunks/{main-app-929a26b3c8cd3f7a.js → main-app-7a1376166cb8b72e.js} +1 -1
- package/dist/src/web/nextui/_next/static/chunks/{webpack-c9f728822666f852.js → webpack-ee33d95b3e219985.js} +1 -1
- package/dist/src/web/nextui/_next/static/css/13f881e4290b6d59.css +1 -0
- package/dist/src/web/nextui/_next/static/css/87b5e6e04efd27e5.css +1 -0
- package/dist/src/web/nextui/_next/static/css/e17fdafd8599f69b.css +1 -0
- package/dist/src/web/nextui/_next/static/css/eb523daddb89dfc1.css +1 -0
- package/dist/src/web/nextui/auth/login/index.html +1 -1
- package/dist/src/web/nextui/auth/login/index.txt +6 -6
- package/dist/src/web/nextui/auth/signup/index.html +1 -1
- package/dist/src/web/nextui/auth/signup/index.txt +6 -6
- package/dist/src/web/nextui/datasets/index.html +1 -1
- package/dist/src/web/nextui/datasets/index.txt +6 -6
- package/dist/src/web/nextui/eval/index.html +1 -1
- package/dist/src/web/nextui/eval/index.txt +8 -8
- package/dist/src/web/nextui/index.html +1 -1
- package/dist/src/web/nextui/index.txt +5 -5
- package/dist/src/web/nextui/logo-panda.svg +91 -0
- package/dist/src/web/nextui/progress/index.html +1 -1
- package/dist/src/web/nextui/progress/index.txt +6 -6
- package/dist/src/web/nextui/prompts/index.html +1 -1
- package/dist/src/web/nextui/prompts/index.txt +6 -6
- package/dist/src/web/nextui/report/index.html +1 -1
- package/dist/src/web/nextui/report/index.txt +8 -8
- package/dist/src/web/nextui/setup/index.html +1 -1
- package/dist/src/web/nextui/setup/index.txt +7 -7
- package/dist/test/__mocks__/tempCustomModule.d.ts +13 -0
- package/dist/test/__mocks__/tempCustomModule.d.ts.map +1 -0
- package/dist/test/__mocks__/tempCustomModule.js +15 -0
- package/dist/test/__mocks__/tempCustomModule.js.map +1 -0
- package/dist/test/account.test.d.ts +2 -0
- package/dist/test/account.test.d.ts.map +1 -0
- package/dist/test/account.test.js +46 -0
- package/dist/test/account.test.js.map +1 -0
- package/dist/test/assertions/AssertionResult.test.d.ts +2 -0
- package/dist/test/assertions/AssertionResult.test.d.ts.map +1 -0
- package/dist/test/assertions/AssertionResult.test.js +184 -0
- package/dist/test/assertions/AssertionResult.test.js.map +1 -0
- package/dist/test/assertions/validateAssertions.test.d.ts +2 -0
- package/dist/test/assertions/validateAssertions.test.d.ts.map +1 -0
- package/dist/test/assertions/validateAssertions.test.js +40 -0
- package/dist/test/assertions/validateAssertions.test.js.map +1 -0
- package/dist/test/assertions.test.d.ts +2 -0
- package/dist/test/assertions.test.d.ts.map +1 -0
- package/dist/test/assertions.test.js +3162 -0
- package/dist/test/assertions.test.js.map +1 -0
- package/dist/test/cache.test.d.ts +2 -0
- package/dist/test/cache.test.d.ts.map +1 -0
- package/dist/test/cache.test.js +90 -0
- package/dist/test/cache.test.js.map +1 -0
- package/dist/test/checkNodeVersion.test.d.ts +2 -0
- package/dist/test/checkNodeVersion.test.d.ts.map +1 -0
- package/dist/test/checkNodeVersion.test.js +85 -0
- package/dist/test/checkNodeVersion.test.js.map +1 -0
- package/dist/test/commands/eval/filterFailingTests.test.d.ts +2 -0
- package/dist/test/commands/eval/filterFailingTests.test.d.ts.map +1 -0
- package/dist/test/commands/eval/filterFailingTests.test.js +84 -0
- package/dist/test/commands/eval/filterFailingTests.test.js.map +1 -0
- package/dist/test/commands/eval/filterProviders.test.d.ts +2 -0
- package/dist/test/commands/eval/filterProviders.test.d.ts.map +1 -0
- package/dist/test/commands/eval/filterProviders.test.js +50 -0
- package/dist/test/commands/eval/filterProviders.test.js.map +1 -0
- package/dist/test/commands/eval/filterTests.test.d.ts +2 -0
- package/dist/test/commands/eval/filterTests.test.d.ts.map +1 -0
- package/dist/test/commands/eval/filterTests.test.js +51 -0
- package/dist/test/commands/eval/filterTests.test.js.map +1 -0
- package/dist/test/config.test.d.ts +2 -0
- package/dist/test/config.test.d.ts.map +1 -0
- package/dist/test/config.test.js +404 -0
- package/dist/test/config.test.js.map +1 -0
- package/dist/test/csv.test.d.ts +2 -0
- package/dist/test/csv.test.d.ts.map +1 -0
- package/dist/test/csv.test.js +239 -0
- package/dist/test/csv.test.js.map +1 -0
- package/dist/test/evaluator.test.d.ts +2 -0
- package/dist/test/evaluator.test.d.ts.map +1 -0
- package/dist/test/evaluator.test.js +922 -0
- package/dist/test/evaluator.test.js.map +1 -0
- package/dist/test/globalConfig.test.d.ts +2 -0
- package/dist/test/globalConfig.test.d.ts.map +1 -0
- package/dist/test/globalConfig.test.js +91 -0
- package/dist/test/globalConfig.test.js.map +1 -0
- package/dist/test/is-sql-tests/node-sql-parser.test.d.ts +2 -0
- package/dist/test/is-sql-tests/node-sql-parser.test.d.ts.map +1 -0
- package/dist/test/is-sql-tests/node-sql-parser.test.js +179 -0
- package/dist/test/is-sql-tests/node-sql-parser.test.js.map +1 -0
- package/dist/test/matchers.test.d.ts +2 -0
- package/dist/test/matchers.test.d.ts.map +1 -0
- package/dist/test/matchers.test.js +807 -0
- package/dist/test/matchers.test.js.map +1 -0
- package/dist/test/prompts.processors.javascript.test.d.ts +2 -0
- package/dist/test/prompts.processors.javascript.test.d.ts.map +1 -0
- package/dist/test/prompts.processors.javascript.test.js +93 -0
- package/dist/test/prompts.processors.javascript.test.js.map +1 -0
- package/dist/test/prompts.processors.json.test.d.ts +2 -0
- package/dist/test/prompts.processors.json.test.d.ts.map +1 -0
- package/dist/test/prompts.processors.json.test.js +67 -0
- package/dist/test/prompts.processors.json.test.js.map +1 -0
- package/dist/test/prompts.processors.jsonl.test.d.ts +2 -0
- package/dist/test/prompts.processors.jsonl.test.d.ts.map +1 -0
- package/dist/test/prompts.processors.jsonl.test.js +99 -0
- package/dist/test/prompts.processors.jsonl.test.js.map +1 -0
- package/dist/test/prompts.processors.python.test.d.ts +2 -0
- package/dist/test/prompts.processors.python.test.d.ts.map +1 -0
- package/dist/test/prompts.processors.python.test.js +100 -0
- package/dist/test/prompts.processors.python.test.js.map +1 -0
- package/dist/test/prompts.processors.python.utils.test.d.ts +2 -0
- package/dist/test/prompts.processors.python.utils.test.d.ts.map +1 -0
- package/dist/test/prompts.processors.python.utils.test.js +68 -0
- package/dist/test/prompts.processors.python.utils.test.js.map +1 -0
- package/dist/test/prompts.processors.string.test.d.ts +2 -0
- package/dist/test/prompts.processors.string.test.d.ts.map +1 -0
- package/dist/test/prompts.processors.string.test.js +24 -0
- package/dist/test/prompts.processors.string.test.js.map +1 -0
- package/dist/test/prompts.processors.text.test.d.ts +2 -0
- package/dist/test/prompts.processors.text.test.d.ts.map +1 -0
- package/dist/test/prompts.processors.text.test.js +109 -0
- package/dist/test/prompts.processors.text.test.js.map +1 -0
- package/dist/test/prompts.processors.yaml.test.d.ts +2 -0
- package/dist/test/prompts.processors.yaml.test.d.ts.map +1 -0
- package/dist/test/prompts.processors.yaml.test.js +76 -0
- package/dist/test/prompts.processors.yaml.test.js.map +1 -0
- package/dist/test/prompts.test.d.ts +2 -0
- package/dist/test/prompts.test.d.ts.map +1 -0
- package/dist/test/prompts.test.js +562 -0
- package/dist/test/prompts.test.js.map +1 -0
- package/dist/test/prompts.utils.test.d.ts +2 -0
- package/dist/test/prompts.utils.test.d.ts.map +1 -0
- package/dist/test/prompts.utils.test.js +123 -0
- package/dist/test/prompts.utils.test.js.map +1 -0
- package/dist/test/providers.anthropic.test.d.ts +2 -0
- package/dist/test/providers.anthropic.test.d.ts.map +1 -0
- package/dist/test/providers.anthropic.test.js +520 -0
- package/dist/test/providers.anthropic.test.js.map +1 -0
- package/dist/test/providers.azure.test.d.ts +2 -0
- package/dist/test/providers.azure.test.d.ts.map +1 -0
- package/dist/test/providers.azure.test.js +96 -0
- package/dist/test/providers.azure.test.js.map +1 -0
- package/dist/test/providers.bedrock.test.d.ts +2 -0
- package/dist/test/providers.bedrock.test.d.ts.map +1 -0
- package/dist/test/providers.bedrock.test.js +349 -0
- package/dist/test/providers.bedrock.test.js.map +1 -0
- package/dist/test/providers.http.test.d.ts +2 -0
- package/dist/test/providers.http.test.d.ts.map +1 -0
- package/dist/test/providers.http.test.js +130 -0
- package/dist/test/providers.http.test.js.map +1 -0
- package/dist/test/providers.llama.test.d.ts +2 -0
- package/dist/test/providers.llama.test.d.ts.map +1 -0
- package/dist/test/providers.llama.test.js +101 -0
- package/dist/test/providers.llama.test.js.map +1 -0
- package/dist/test/providers.pythonCompletion.test.d.ts +2 -0
- package/dist/test/providers.pythonCompletion.test.d.ts.map +1 -0
- package/dist/test/providers.pythonCompletion.test.js +149 -0
- package/dist/test/providers.pythonCompletion.test.js.map +1 -0
- package/dist/test/providers.test.d.ts +2 -0
- package/dist/test/providers.test.d.ts.map +1 -0
- package/dist/test/providers.test.js +883 -0
- package/dist/test/providers.test.js.map +1 -0
- package/dist/test/providers.vertex.test.d.ts +2 -0
- package/dist/test/providers.vertex.test.d.ts.map +1 -0
- package/dist/test/providers.vertex.test.js +153 -0
- package/dist/test/providers.vertex.test.js.map +1 -0
- package/dist/test/pythonWrapper.test.d.ts +2 -0
- package/dist/test/pythonWrapper.test.d.ts.map +1 -0
- package/dist/test/pythonWrapper.test.js +65 -0
- package/dist/test/pythonWrapper.test.js.map +1 -0
- package/dist/test/redteam/plugins/base.test.d.ts +2 -0
- package/dist/test/redteam/plugins/base.test.d.ts.map +1 -0
- package/dist/test/redteam/plugins/base.test.js +149 -0
- package/dist/test/redteam/plugins/base.test.js.map +1 -0
- package/dist/test/redteam/purpose.test.d.ts +2 -0
- package/dist/test/redteam/purpose.test.d.ts.map +1 -0
- package/dist/test/redteam/purpose.test.js +37 -0
- package/dist/test/redteam/purpose.test.js.map +1 -0
- package/dist/test/redteam/types.test.d.ts +2 -0
- package/dist/test/redteam/types.test.d.ts.map +1 -0
- package/dist/test/redteam/types.test.js +325 -0
- package/dist/test/redteam/types.test.js.map +1 -0
- package/dist/test/redteam/util.test.d.ts +2 -0
- package/dist/test/redteam/util.test.d.ts.map +1 -0
- package/dist/test/redteam/util.test.js +99 -0
- package/dist/test/redteam/util.test.js.map +1 -0
- package/dist/test/telemetry.test.d.ts +2 -0
- package/dist/test/telemetry.test.d.ts.map +1 -0
- package/dist/test/telemetry.test.js +68 -0
- package/dist/test/telemetry.test.js.map +1 -0
- package/dist/test/testCases.test.d.ts +2 -0
- package/dist/test/testCases.test.d.ts.map +1 -0
- package/dist/test/testCases.test.js +362 -0
- package/dist/test/testCases.test.js.map +1 -0
- package/dist/test/updates.test.d.ts +2 -0
- package/dist/test/updates.test.d.ts.map +1 -0
- package/dist/test/updates.test.js +63 -0
- package/dist/test/updates.test.js.map +1 -0
- package/dist/test/util.templates.test.d.ts +2 -0
- package/dist/test/util.templates.test.d.ts.map +1 -0
- package/dist/test/util.templates.test.js +75 -0
- package/dist/test/util.templates.test.js.map +1 -0
- package/dist/test/util.test.d.ts +2 -0
- package/dist/test/util.test.d.ts.map +1 -0
- package/dist/test/util.test.js +661 -0
- package/dist/test/util.test.js.map +1 -0
- package/dist/test/utils.d.ts +6 -0
- package/dist/test/utils.d.ts.map +1 -0
- package/dist/test/utils.js +16 -0
- package/dist/test/utils.js.map +1 -0
- package/package.json +14 -8
- package/dist/src/database.js.map +0 -1
- package/dist/src/redteam/methods/injections.d.ts.map +0 -1
- package/dist/src/redteam/methods/injections.js.map +0 -1
- package/dist/src/redteam/methods/iterative.d.ts.map +0 -1
- package/dist/src/redteam/methods/iterative.js.map +0 -1
- package/dist/src/types.js.map +0 -1
- package/dist/src/web/nextui/_next/static/chunks/163-e65e0f7f442a0c72.js +0 -6
- package/dist/src/web/nextui/_next/static/chunks/180-46db19289d856800.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/304-9d57a2251034b801.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/322-4a0fc9b0508f47e8.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/339-db5bd05e24dde905.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/374-d40afe599198abd3.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/378-e22c8aeb8e031fad.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/448-4da3bf74eae4996a.js +0 -97
- package/dist/src/web/nextui/_next/static/chunks/53-fae6e50ace6c83a1.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/578-8efe57f906f7573c.js +0 -44
- package/dist/src/web/nextui/_next/static/chunks/620-7a5a7bf6e1fe49e5.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/737-538f50dc31cc8c49.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/905-0da344cee75690e7.js +0 -32
- package/dist/src/web/nextui/_next/static/chunks/932-75585b3be8ce494d.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/973-ffe3726c956d08ef.js +0 -2
- package/dist/src/web/nextui/_next/static/chunks/995-803c74b81e7bf6cd.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/app/auth/login/page-2e2c0c725127ea2d.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/app/auth/signup/page-ccdb1f6890601666.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/app/datasets/page-642db4f4a8e8ba40.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/app/eval/[id]/not-found-ce320e6d1e6d1d23.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/app/eval/page-32eaa14d2384c5b0.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/app/layout-4282b1d33566e258.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/app/progress/page-462526776efd6fd6.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/app/prompts/page-0ae78bc0bf7c56bc.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/app/report/page-e4f7851d41eb0f92.js +0 -1
- package/dist/src/web/nextui/_next/static/chunks/app/setup/page-6cde750ec428cd75.js +0 -1
- package/dist/src/web/nextui/_next/static/css/35ab7ce8025438b0.css +0 -1
- package/dist/src/web/nextui/_next/static/css/5bd2f45de1f3ba83.css +0 -1
- package/dist/src/web/nextui/_next/static/css/94b771e23fc5d5f5.css +0 -1
- package/dist/src/web/nextui/_next/static/css/dab5d695b3657d59.css +0 -1
- /package/dist/src/redteam/{methods → strategies}/injections.d.ts +0 -0
- /package/dist/src/redteam/{methods → strategies}/iterative.d.ts +0 -0
- /package/dist/src/redteam/{methods → strategies}/iterative.js +0 -0
- /package/dist/src/web/nextui/_next/static/{8E4q0Tmu22G1wENZyXEq0 → L-tpppAe26U-euPv62afH}/_buildManifest.js +0 -0
- /package/dist/src/web/nextui/_next/static/{8E4q0Tmu22G1wENZyXEq0 → L-tpppAe26U-euPv62afH}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,807 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const matchers_1 = require("../src/matchers");
|
|
4
|
+
const matchers_2 = require("../src/matchers");
|
|
5
|
+
const prompts_1 = require("../src/prompts");
|
|
6
|
+
const huggingface_1 = require("../src/providers/huggingface");
|
|
7
|
+
const openai_1 = require("../src/providers/openai");
|
|
8
|
+
const openai_2 = require("../src/providers/openai");
|
|
9
|
+
const utils_1 = require("./utils");
|
|
10
|
+
jest.mock('../src/esm');
|
|
11
|
+
const Grader = new utils_1.TestGrader();
|
|
12
|
+
describe('matchesSimilarity', () => {
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
jest.spyOn(openai_2.DefaultEmbeddingProvider, 'callEmbeddingApi').mockImplementation((text) => {
|
|
15
|
+
if (text === 'Expected output' || text === 'Sample output') {
|
|
16
|
+
return Promise.resolve({
|
|
17
|
+
embedding: [1, 0, 0],
|
|
18
|
+
tokenUsage: { total: 5, prompt: 2, completion: 3 },
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
else if (text === 'Different output') {
|
|
22
|
+
return Promise.resolve({
|
|
23
|
+
embedding: [0, 1, 0],
|
|
24
|
+
tokenUsage: { total: 5, prompt: 2, completion: 3 },
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return Promise.reject(new Error('Unexpected input'));
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
afterEach(() => {
|
|
31
|
+
jest.restoreAllMocks();
|
|
32
|
+
});
|
|
33
|
+
it('should pass when similarity is above the threshold', async () => {
|
|
34
|
+
const expected = 'Expected output';
|
|
35
|
+
const output = 'Sample output';
|
|
36
|
+
const threshold = 0.5;
|
|
37
|
+
await expect((0, matchers_2.matchesSimilarity)(expected, output, threshold)).resolves.toEqual({
|
|
38
|
+
pass: true,
|
|
39
|
+
reason: 'Similarity 1.00 is greater than threshold 0.5',
|
|
40
|
+
score: 1,
|
|
41
|
+
tokensUsed: {
|
|
42
|
+
total: expect.any(Number),
|
|
43
|
+
prompt: expect.any(Number),
|
|
44
|
+
completion: expect.any(Number),
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
it('should fail when similarity is below the threshold', async () => {
|
|
49
|
+
const expected = 'Expected output';
|
|
50
|
+
const output = 'Different output';
|
|
51
|
+
const threshold = 0.9;
|
|
52
|
+
await expect((0, matchers_2.matchesSimilarity)(expected, output, threshold)).resolves.toEqual({
|
|
53
|
+
pass: false,
|
|
54
|
+
reason: 'Similarity 0.00 is less than threshold 0.9',
|
|
55
|
+
score: 0,
|
|
56
|
+
tokensUsed: {
|
|
57
|
+
total: expect.any(Number),
|
|
58
|
+
prompt: expect.any(Number),
|
|
59
|
+
completion: expect.any(Number),
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
it('should fail when inverted similarity is above the threshold', async () => {
|
|
64
|
+
const expected = 'Expected output';
|
|
65
|
+
const output = 'Sample output';
|
|
66
|
+
const threshold = 0.5;
|
|
67
|
+
await expect((0, matchers_2.matchesSimilarity)(expected, output, threshold, true /* invert */)).resolves.toEqual({
|
|
68
|
+
pass: false,
|
|
69
|
+
reason: 'Similarity 1.00 is greater than threshold 0.5',
|
|
70
|
+
score: 0,
|
|
71
|
+
tokensUsed: {
|
|
72
|
+
total: expect.any(Number),
|
|
73
|
+
prompt: expect.any(Number),
|
|
74
|
+
completion: expect.any(Number),
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
it('should pass when inverted similarity is below the threshold', async () => {
|
|
79
|
+
const expected = 'Expected output';
|
|
80
|
+
const output = 'Different output';
|
|
81
|
+
const threshold = 0.9;
|
|
82
|
+
await expect((0, matchers_2.matchesSimilarity)(expected, output, threshold, true /* invert */)).resolves.toEqual({
|
|
83
|
+
pass: true,
|
|
84
|
+
reason: 'Similarity 0.00 is less than threshold 0.9',
|
|
85
|
+
score: 1,
|
|
86
|
+
tokensUsed: {
|
|
87
|
+
total: expect.any(Number),
|
|
88
|
+
prompt: expect.any(Number),
|
|
89
|
+
completion: expect.any(Number),
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
it('should use the overridden similarity grading config', async () => {
|
|
94
|
+
const expected = 'Expected output';
|
|
95
|
+
const output = 'Sample output';
|
|
96
|
+
const threshold = 0.5;
|
|
97
|
+
const grading = {
|
|
98
|
+
provider: {
|
|
99
|
+
id: 'openai:embedding:text-embedding-ada-9999999',
|
|
100
|
+
config: {
|
|
101
|
+
apiKey: 'abc123',
|
|
102
|
+
temperature: 3.1415926,
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
const mockCallApi = jest.spyOn(openai_1.OpenAiEmbeddingProvider.prototype, 'callEmbeddingApi');
|
|
107
|
+
mockCallApi.mockImplementation(function () {
|
|
108
|
+
expect(this.config.temperature).toBe(3.1415926);
|
|
109
|
+
expect(this.getApiKey()).toBe('abc123');
|
|
110
|
+
return Promise.resolve({
|
|
111
|
+
embedding: [1, 0, 0],
|
|
112
|
+
tokenUsage: { total: 5, prompt: 2, completion: 3 },
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
await expect((0, matchers_2.matchesSimilarity)(expected, output, threshold, false, grading)).resolves.toEqual({
|
|
116
|
+
pass: true,
|
|
117
|
+
reason: 'Similarity 1.00 is greater than threshold 0.5',
|
|
118
|
+
score: 1,
|
|
119
|
+
tokensUsed: {
|
|
120
|
+
total: expect.any(Number),
|
|
121
|
+
prompt: expect.any(Number),
|
|
122
|
+
completion: expect.any(Number),
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
expect(mockCallApi).toHaveBeenCalledWith('Expected output');
|
|
126
|
+
mockCallApi.mockRestore();
|
|
127
|
+
});
|
|
128
|
+
it('should throw an error when API call fails', async () => {
|
|
129
|
+
const expected = 'Expected output';
|
|
130
|
+
const output = 'Sample output';
|
|
131
|
+
const threshold = 0.5;
|
|
132
|
+
const grading = {
|
|
133
|
+
provider: {
|
|
134
|
+
id: 'openai:embedding:text-embedding-ada-9999999',
|
|
135
|
+
config: {
|
|
136
|
+
apiKey: 'abc123',
|
|
137
|
+
temperature: 3.1415926,
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
jest
|
|
142
|
+
.spyOn(openai_1.OpenAiEmbeddingProvider.prototype, 'callEmbeddingApi')
|
|
143
|
+
.mockRejectedValueOnce(new Error('API call failed'));
|
|
144
|
+
await expect(async () => {
|
|
145
|
+
await (0, matchers_2.matchesSimilarity)(expected, output, threshold, false, grading);
|
|
146
|
+
}).rejects.toThrow('API call failed');
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
describe('matchesLlmRubric', () => {
|
|
150
|
+
it('should pass when the grading provider returns a passing result', async () => {
|
|
151
|
+
const expected = 'Expected output';
|
|
152
|
+
const output = 'Sample output';
|
|
153
|
+
const options = {
|
|
154
|
+
rubricPrompt: 'Grading prompt',
|
|
155
|
+
provider: Grader,
|
|
156
|
+
};
|
|
157
|
+
await expect((0, matchers_2.matchesLlmRubric)(expected, output, options)).resolves.toEqual({
|
|
158
|
+
pass: true,
|
|
159
|
+
reason: 'Test grading output',
|
|
160
|
+
score: 1,
|
|
161
|
+
tokensUsed: {
|
|
162
|
+
total: expect.any(Number),
|
|
163
|
+
prompt: expect.any(Number),
|
|
164
|
+
completion: expect.any(Number),
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
it('should fail when the grading provider returns a failing result', async () => {
|
|
169
|
+
const expected = 'Expected output';
|
|
170
|
+
const output = 'Different output';
|
|
171
|
+
const options = {
|
|
172
|
+
rubricPrompt: 'Grading prompt',
|
|
173
|
+
provider: Grader,
|
|
174
|
+
};
|
|
175
|
+
jest.spyOn(Grader, 'callApi').mockResolvedValueOnce({
|
|
176
|
+
output: JSON.stringify({ pass: false, reason: 'Grading failed' }),
|
|
177
|
+
tokenUsage: { total: 10, prompt: 5, completion: 5 },
|
|
178
|
+
});
|
|
179
|
+
await expect((0, matchers_2.matchesLlmRubric)(expected, output, options)).resolves.toEqual({
|
|
180
|
+
pass: false,
|
|
181
|
+
reason: 'Grading failed',
|
|
182
|
+
score: 0,
|
|
183
|
+
tokensUsed: {
|
|
184
|
+
total: expect.any(Number),
|
|
185
|
+
prompt: expect.any(Number),
|
|
186
|
+
completion: expect.any(Number),
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
it('should use the overridden llm rubric grading config', async () => {
|
|
191
|
+
const expected = 'Expected output';
|
|
192
|
+
const output = 'Sample output';
|
|
193
|
+
const options = {
|
|
194
|
+
rubricPrompt: 'Grading prompt',
|
|
195
|
+
provider: {
|
|
196
|
+
id: 'openai:gpt-4o-mini',
|
|
197
|
+
config: {
|
|
198
|
+
apiKey: 'abc123',
|
|
199
|
+
temperature: 3.1415926,
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
const mockCallApi = jest.spyOn(openai_1.OpenAiChatCompletionProvider.prototype, 'callApi');
|
|
204
|
+
mockCallApi.mockImplementation(function () {
|
|
205
|
+
expect(this.config.temperature).toBe(3.1415926);
|
|
206
|
+
expect(this.getApiKey()).toBe('abc123');
|
|
207
|
+
return Promise.resolve({
|
|
208
|
+
output: JSON.stringify({ pass: true, reason: 'Grading passed' }),
|
|
209
|
+
tokenUsage: { total: 10, prompt: 5, completion: 5 },
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
await expect((0, matchers_2.matchesLlmRubric)(expected, output, options)).resolves.toEqual({
|
|
213
|
+
reason: 'Grading passed',
|
|
214
|
+
pass: true,
|
|
215
|
+
score: 1,
|
|
216
|
+
tokensUsed: {
|
|
217
|
+
total: expect.any(Number),
|
|
218
|
+
prompt: expect.any(Number),
|
|
219
|
+
completion: expect.any(Number),
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
expect(mockCallApi).toHaveBeenCalledWith('Grading prompt');
|
|
223
|
+
mockCallApi.mockRestore();
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
describe('matchesFactuality', () => {
|
|
227
|
+
it('should pass when the factuality check passes', async () => {
|
|
228
|
+
const input = 'Input text';
|
|
229
|
+
const expected = 'Expected output';
|
|
230
|
+
const output = 'Sample output';
|
|
231
|
+
const grading = {};
|
|
232
|
+
jest.spyOn(openai_2.DefaultGradingProvider, 'callApi').mockResolvedValueOnce({
|
|
233
|
+
output: '(A) The submitted answer is a subset of the expert answer and is fully consistent with it.',
|
|
234
|
+
tokenUsage: { total: 10, prompt: 5, completion: 5 },
|
|
235
|
+
});
|
|
236
|
+
await expect((0, matchers_2.matchesFactuality)(input, expected, output, grading)).resolves.toEqual({
|
|
237
|
+
pass: true,
|
|
238
|
+
reason: 'The submitted answer is a subset of the expert answer and is fully consistent with it.',
|
|
239
|
+
score: 1,
|
|
240
|
+
tokensUsed: {
|
|
241
|
+
total: expect.any(Number),
|
|
242
|
+
prompt: expect.any(Number),
|
|
243
|
+
completion: expect.any(Number),
|
|
244
|
+
},
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
it('should fail when the factuality check fails', async () => {
|
|
248
|
+
const input = 'Input text';
|
|
249
|
+
const expected = 'Expected output';
|
|
250
|
+
const output = 'Sample output';
|
|
251
|
+
const grading = {};
|
|
252
|
+
jest.spyOn(openai_2.DefaultGradingProvider, 'callApi').mockResolvedValueOnce({
|
|
253
|
+
output: '(D) There is a disagreement between the submitted answer and the expert answer.',
|
|
254
|
+
tokenUsage: { total: 10, prompt: 5, completion: 5 },
|
|
255
|
+
});
|
|
256
|
+
await expect((0, matchers_2.matchesFactuality)(input, expected, output, grading)).resolves.toEqual({
|
|
257
|
+
pass: false,
|
|
258
|
+
reason: 'There is a disagreement between the submitted answer and the expert answer.',
|
|
259
|
+
score: 0,
|
|
260
|
+
tokensUsed: {
|
|
261
|
+
total: expect.any(Number),
|
|
262
|
+
prompt: expect.any(Number),
|
|
263
|
+
completion: expect.any(Number),
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
it('should use the overridden factuality grading config', async () => {
|
|
268
|
+
const input = 'Input text';
|
|
269
|
+
const expected = 'Expected output';
|
|
270
|
+
const output = 'Sample output';
|
|
271
|
+
const grading = {
|
|
272
|
+
factuality: {
|
|
273
|
+
subset: 0.8,
|
|
274
|
+
superset: 0.9,
|
|
275
|
+
agree: 1,
|
|
276
|
+
disagree: 0,
|
|
277
|
+
differButFactual: 0.7,
|
|
278
|
+
},
|
|
279
|
+
};
|
|
280
|
+
jest.spyOn(openai_2.DefaultGradingProvider, 'callApi').mockResolvedValueOnce({
|
|
281
|
+
output: '(A) The submitted answer is a subset of the expert answer and is fully consistent with it.',
|
|
282
|
+
tokenUsage: { total: 10, prompt: 5, completion: 5 },
|
|
283
|
+
});
|
|
284
|
+
await expect((0, matchers_2.matchesFactuality)(input, expected, output, grading)).resolves.toEqual({
|
|
285
|
+
pass: true,
|
|
286
|
+
reason: 'The submitted answer is a subset of the expert answer and is fully consistent with it.',
|
|
287
|
+
score: 0.8,
|
|
288
|
+
tokensUsed: {
|
|
289
|
+
total: expect.any(Number),
|
|
290
|
+
prompt: expect.any(Number),
|
|
291
|
+
completion: expect.any(Number),
|
|
292
|
+
},
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
it('should throw an error when an error occurs', async () => {
|
|
296
|
+
const input = 'Input text';
|
|
297
|
+
const expected = 'Expected output';
|
|
298
|
+
const output = 'Sample output';
|
|
299
|
+
const grading = {};
|
|
300
|
+
jest.spyOn(openai_2.DefaultGradingProvider, 'callApi').mockImplementation(() => {
|
|
301
|
+
throw new Error('An error occurred');
|
|
302
|
+
});
|
|
303
|
+
await expect((0, matchers_2.matchesFactuality)(input, expected, output, grading)).rejects.toThrow('An error occurred');
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
describe('matchesClosedQa', () => {
|
|
307
|
+
it('should pass when the closed QA check passes', async () => {
|
|
308
|
+
const input = 'Input text';
|
|
309
|
+
const expected = 'Expected output';
|
|
310
|
+
const output = 'Sample output';
|
|
311
|
+
const grading = {};
|
|
312
|
+
jest.spyOn(openai_2.DefaultGradingProvider, 'callApi').mockResolvedValueOnce({
|
|
313
|
+
output: 'foo \n \n bar\n Y Y',
|
|
314
|
+
tokenUsage: { total: 10, prompt: 5, completion: 5 },
|
|
315
|
+
});
|
|
316
|
+
await expect((0, matchers_2.matchesClosedQa)(input, expected, output, grading)).resolves.toEqual({
|
|
317
|
+
pass: true,
|
|
318
|
+
reason: 'The submission meets the criterion',
|
|
319
|
+
score: 1,
|
|
320
|
+
tokensUsed: {
|
|
321
|
+
total: expect.any(Number),
|
|
322
|
+
prompt: expect.any(Number),
|
|
323
|
+
completion: expect.any(Number),
|
|
324
|
+
},
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
it('should fail when the closed QA check fails', async () => {
|
|
328
|
+
const input = 'Input text';
|
|
329
|
+
const expected = 'Expected output';
|
|
330
|
+
const output = 'Sample output';
|
|
331
|
+
const grading = {};
|
|
332
|
+
jest.spyOn(openai_2.DefaultGradingProvider, 'callApi').mockResolvedValueOnce({
|
|
333
|
+
output: 'foo bar N',
|
|
334
|
+
tokenUsage: { total: 10, prompt: 5, completion: 5 },
|
|
335
|
+
});
|
|
336
|
+
await expect((0, matchers_2.matchesClosedQa)(input, expected, output, grading)).resolves.toEqual({
|
|
337
|
+
pass: false,
|
|
338
|
+
reason: 'The submission does not meet the criterion:\nfoo bar N',
|
|
339
|
+
score: 0,
|
|
340
|
+
tokensUsed: {
|
|
341
|
+
total: expect.any(Number),
|
|
342
|
+
prompt: expect.any(Number),
|
|
343
|
+
completion: expect.any(Number),
|
|
344
|
+
},
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
it('should throw an error when an error occurs', async () => {
|
|
348
|
+
const input = 'Input text';
|
|
349
|
+
const expected = 'Expected output';
|
|
350
|
+
const output = 'Sample output';
|
|
351
|
+
const grading = {};
|
|
352
|
+
jest.spyOn(openai_2.DefaultGradingProvider, 'callApi').mockImplementation(() => {
|
|
353
|
+
throw new Error('An error occurred');
|
|
354
|
+
});
|
|
355
|
+
await expect((0, matchers_2.matchesClosedQa)(input, expected, output, grading)).rejects.toThrow('An error occurred');
|
|
356
|
+
});
|
|
357
|
+
it('should handle input, criteria, and completion that need escaping', async () => {
|
|
358
|
+
const input = 'Input "text" with \\ escape characters and \\"nested\\" escapes';
|
|
359
|
+
const expected = 'Expected "output" with \\\\ escape characters and \\"nested\\" escapes';
|
|
360
|
+
const output = 'Sample "output" with \\\\ escape characters and \\"nested\\" escapes';
|
|
361
|
+
const grading = {};
|
|
362
|
+
let isJson = false;
|
|
363
|
+
jest.spyOn(openai_2.DefaultGradingProvider, 'callApi').mockImplementation((prompt) => {
|
|
364
|
+
try {
|
|
365
|
+
JSON.parse(prompt);
|
|
366
|
+
isJson = true;
|
|
367
|
+
}
|
|
368
|
+
catch (err) {
|
|
369
|
+
isJson = false;
|
|
370
|
+
}
|
|
371
|
+
return Promise.resolve({
|
|
372
|
+
output: 'foo \n \n bar\n Y Y',
|
|
373
|
+
tokenUsage: { total: 10, prompt: 5, completion: 5 },
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
await expect((0, matchers_2.matchesClosedQa)(input, expected, output, grading)).resolves.toEqual({
|
|
377
|
+
pass: true,
|
|
378
|
+
reason: 'The submission meets the criterion',
|
|
379
|
+
score: 1,
|
|
380
|
+
tokensUsed: {
|
|
381
|
+
total: expect.any(Number),
|
|
382
|
+
prompt: expect.any(Number),
|
|
383
|
+
completion: expect.any(Number),
|
|
384
|
+
},
|
|
385
|
+
});
|
|
386
|
+
expect(isJson).toBeTruthy();
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
describe('getGradingProvider', () => {
|
|
390
|
+
it('should return the correct provider when provider is a string', async () => {
|
|
391
|
+
const provider = await (0, matchers_1.getGradingProvider)('text', 'openai:chat:gpt-3.5-turbo-foobar', openai_2.DefaultGradingProvider);
|
|
392
|
+
// ok for this not to match exactly when the string is parsed
|
|
393
|
+
expect(provider?.id()).toBe('openai:gpt-3.5-turbo-foobar');
|
|
394
|
+
});
|
|
395
|
+
it('should return the correct provider when provider is an ApiProvider', async () => {
|
|
396
|
+
const provider = await (0, matchers_1.getGradingProvider)('embedding', openai_2.DefaultEmbeddingProvider, openai_2.DefaultGradingProvider);
|
|
397
|
+
expect(provider).toBe(openai_2.DefaultEmbeddingProvider);
|
|
398
|
+
});
|
|
399
|
+
it('should return the correct provider when provider is ProviderOptions', async () => {
|
|
400
|
+
const providerOptions = {
|
|
401
|
+
id: 'openai:chat:gpt-3.5-turbo-foobar',
|
|
402
|
+
config: {
|
|
403
|
+
apiKey: 'abc123',
|
|
404
|
+
temperature: 3.1415926,
|
|
405
|
+
},
|
|
406
|
+
};
|
|
407
|
+
const provider = await (0, matchers_1.getGradingProvider)('text', providerOptions, openai_2.DefaultGradingProvider);
|
|
408
|
+
expect(provider?.id()).toBe('openai:chat:gpt-3.5-turbo-foobar');
|
|
409
|
+
});
|
|
410
|
+
it('should return the default provider when provider is not provided', async () => {
|
|
411
|
+
const provider = await (0, matchers_1.getGradingProvider)('text', undefined, openai_2.DefaultGradingProvider);
|
|
412
|
+
expect(provider).toBe(openai_2.DefaultGradingProvider);
|
|
413
|
+
});
|
|
414
|
+
});
|
|
415
|
+
describe('getAndCheckProvider', () => {
|
|
416
|
+
it('should return the default provider when provider is not defined', async () => {
|
|
417
|
+
await expect((0, matchers_1.getAndCheckProvider)('text', undefined, openai_2.DefaultGradingProvider, 'test check')).resolves.toBe(openai_2.DefaultGradingProvider);
|
|
418
|
+
});
|
|
419
|
+
it('should return the default provider when provider does not support type', async () => {
|
|
420
|
+
const provider = {
|
|
421
|
+
id: () => 'test-provider',
|
|
422
|
+
callApi: () => Promise.resolve({ output: 'test' }),
|
|
423
|
+
};
|
|
424
|
+
await expect((0, matchers_1.getAndCheckProvider)('embedding', provider, openai_2.DefaultEmbeddingProvider, 'test check')).resolves.toBe(openai_2.DefaultEmbeddingProvider);
|
|
425
|
+
});
|
|
426
|
+
it('should return the provider if it implements the required method', async () => {
|
|
427
|
+
const provider = {
|
|
428
|
+
id: () => 'test-provider',
|
|
429
|
+
callApi: () => Promise.resolve({ output: 'test' }),
|
|
430
|
+
callEmbeddingApi: () => Promise.resolve({ embedding: [] }),
|
|
431
|
+
};
|
|
432
|
+
const result = await (0, matchers_1.getAndCheckProvider)('embedding', provider, openai_2.DefaultEmbeddingProvider, 'test check');
|
|
433
|
+
expect(result).toBe(provider);
|
|
434
|
+
});
|
|
435
|
+
it('should return the default provider when no provider is specified', async () => {
|
|
436
|
+
const provider = await (0, matchers_1.getGradingProvider)('text', undefined, openai_2.DefaultGradingProvider);
|
|
437
|
+
expect(provider).toBe(openai_2.DefaultGradingProvider);
|
|
438
|
+
});
|
|
439
|
+
it('should return a specific provider when a provider id is specified', async () => {
|
|
440
|
+
const provider = await (0, matchers_1.getGradingProvider)('text', 'openai:chat:foo', openai_2.DefaultGradingProvider);
|
|
441
|
+
// loadApiProvider removes `chat` from the id
|
|
442
|
+
expect(provider?.id()).toBe('openai:foo');
|
|
443
|
+
});
|
|
444
|
+
it('should return a provider from ApiProvider when specified', async () => {
|
|
445
|
+
const providerOptions = {
|
|
446
|
+
id: () => 'custom-provider',
|
|
447
|
+
callApi: async () => ({}),
|
|
448
|
+
};
|
|
449
|
+
const provider = await (0, matchers_1.getGradingProvider)('text', providerOptions, openai_2.DefaultGradingProvider);
|
|
450
|
+
expect(provider?.id()).toBe('custom-provider');
|
|
451
|
+
});
|
|
452
|
+
it('should return a provider from ProviderTypeMap when specified', async () => {
|
|
453
|
+
const providerTypeMap = {
|
|
454
|
+
text: {
|
|
455
|
+
id: 'openai:chat:foo',
|
|
456
|
+
},
|
|
457
|
+
embedding: {
|
|
458
|
+
id: 'openai:embedding:bar',
|
|
459
|
+
},
|
|
460
|
+
};
|
|
461
|
+
const provider = await (0, matchers_1.getGradingProvider)('text', providerTypeMap, openai_2.DefaultGradingProvider);
|
|
462
|
+
expect(provider?.id()).toBe('openai:chat:foo');
|
|
463
|
+
});
|
|
464
|
+
it('should return a provider from ProviderTypeMap with basic strings', async () => {
|
|
465
|
+
const providerTypeMap = {
|
|
466
|
+
text: 'openai:chat:foo',
|
|
467
|
+
embedding: 'openai:embedding:bar',
|
|
468
|
+
};
|
|
469
|
+
const provider = await (0, matchers_1.getGradingProvider)('text', providerTypeMap, openai_2.DefaultGradingProvider);
|
|
470
|
+
expect(provider?.id()).toBe('openai:foo');
|
|
471
|
+
});
|
|
472
|
+
it('should throw an error when the provider does not match the type', async () => {
|
|
473
|
+
const providerTypeMap = {
|
|
474
|
+
embedding: {
|
|
475
|
+
id: 'openai:embedding:foo',
|
|
476
|
+
},
|
|
477
|
+
};
|
|
478
|
+
await expect((0, matchers_1.getGradingProvider)('text', providerTypeMap, openai_2.DefaultGradingProvider)).rejects.toThrow(new Error(`Invalid provider definition for output type 'text': ${JSON.stringify(providerTypeMap, null, 2)}`));
|
|
479
|
+
});
|
|
480
|
+
});
|
|
481
|
+
describe('matchesAnswerRelevance', () => {
|
|
482
|
+
it('should pass when the relevance score is above the threshold', async () => {
|
|
483
|
+
const input = 'Input text';
|
|
484
|
+
const output = 'Sample output';
|
|
485
|
+
const threshold = 0.5;
|
|
486
|
+
const mockCallApi = jest.spyOn(openai_2.DefaultGradingProvider, 'callApi');
|
|
487
|
+
mockCallApi.mockImplementation(() => {
|
|
488
|
+
return Promise.resolve({
|
|
489
|
+
output: 'foobar',
|
|
490
|
+
tokenUsage: { total: 10, prompt: 5, completion: 5 },
|
|
491
|
+
});
|
|
492
|
+
});
|
|
493
|
+
const mockCallEmbeddingApi = jest.spyOn(openai_2.DefaultEmbeddingProvider, 'callEmbeddingApi');
|
|
494
|
+
mockCallEmbeddingApi.mockImplementation(function () {
|
|
495
|
+
return Promise.resolve({
|
|
496
|
+
embedding: [1, 0, 0],
|
|
497
|
+
tokenUsage: { total: 5, prompt: 2, completion: 3 },
|
|
498
|
+
});
|
|
499
|
+
});
|
|
500
|
+
await expect((0, matchers_2.matchesAnswerRelevance)(input, output, threshold)).resolves.toEqual({
|
|
501
|
+
pass: true,
|
|
502
|
+
reason: 'Relevance 1.00 is greater than threshold 0.5',
|
|
503
|
+
score: 1,
|
|
504
|
+
tokensUsed: {
|
|
505
|
+
total: expect.any(Number),
|
|
506
|
+
prompt: expect.any(Number),
|
|
507
|
+
completion: expect.any(Number),
|
|
508
|
+
},
|
|
509
|
+
});
|
|
510
|
+
expect(mockCallApi).toHaveBeenCalledWith(expect.stringContaining(prompts_1.ANSWER_RELEVANCY_GENERATE.slice(0, 50)));
|
|
511
|
+
expect(mockCallEmbeddingApi).toHaveBeenCalledWith('Input text');
|
|
512
|
+
mockCallApi.mockRestore();
|
|
513
|
+
mockCallEmbeddingApi.mockRestore();
|
|
514
|
+
});
|
|
515
|
+
it('should fail when the relevance score is below the threshold', async () => {
|
|
516
|
+
const input = 'Input text';
|
|
517
|
+
const output = 'Different output';
|
|
518
|
+
const threshold = 0.5;
|
|
519
|
+
const mockCallApi = jest.spyOn(openai_2.DefaultGradingProvider, 'callApi');
|
|
520
|
+
mockCallApi.mockImplementation((text) => {
|
|
521
|
+
return Promise.resolve({
|
|
522
|
+
output: text,
|
|
523
|
+
tokenUsage: { total: 10, prompt: 5, completion: 5 },
|
|
524
|
+
});
|
|
525
|
+
});
|
|
526
|
+
const mockCallEmbeddingApi = jest.spyOn(openai_2.DefaultEmbeddingProvider, 'callEmbeddingApi');
|
|
527
|
+
mockCallEmbeddingApi.mockImplementation((text) => {
|
|
528
|
+
if (text.includes('Input text')) {
|
|
529
|
+
return Promise.resolve({
|
|
530
|
+
embedding: [1, 0, 0],
|
|
531
|
+
tokenUsage: { total: 5, prompt: 2, completion: 3 },
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
else if (text.includes('Different output')) {
|
|
535
|
+
return Promise.resolve({
|
|
536
|
+
embedding: [0, 1, 0],
|
|
537
|
+
tokenUsage: { total: 5, prompt: 2, completion: 3 },
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
return Promise.reject(new Error('Unexpected input ' + text));
|
|
541
|
+
});
|
|
542
|
+
await expect((0, matchers_2.matchesAnswerRelevance)(input, output, threshold)).resolves.toEqual({
|
|
543
|
+
pass: false,
|
|
544
|
+
reason: 'Relevance 0.00 is less than threshold 0.5',
|
|
545
|
+
score: 0,
|
|
546
|
+
tokensUsed: {
|
|
547
|
+
total: expect.any(Number),
|
|
548
|
+
prompt: expect.any(Number),
|
|
549
|
+
completion: expect.any(Number),
|
|
550
|
+
},
|
|
551
|
+
});
|
|
552
|
+
expect(mockCallApi).toHaveBeenCalledWith(expect.stringContaining(prompts_1.ANSWER_RELEVANCY_GENERATE.slice(0, 50)));
|
|
553
|
+
expect(mockCallEmbeddingApi).toHaveBeenCalledWith(expect.stringContaining(prompts_1.ANSWER_RELEVANCY_GENERATE.slice(0, 50)));
|
|
554
|
+
mockCallApi.mockRestore();
|
|
555
|
+
mockCallEmbeddingApi.mockRestore();
|
|
556
|
+
});
|
|
557
|
+
});
|
|
558
|
+
describe('matchesClassification', () => {
|
|
559
|
+
class TestGrader {
|
|
560
|
+
async callApi() {
|
|
561
|
+
throw new Error('Not implemented');
|
|
562
|
+
}
|
|
563
|
+
async callClassificationApi() {
|
|
564
|
+
return {
|
|
565
|
+
classification: {
|
|
566
|
+
classA: 0.6,
|
|
567
|
+
classB: 0.5,
|
|
568
|
+
},
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
id() {
|
|
572
|
+
return 'TestClassificationProvider';
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
it('should pass when the classification score is above the threshold', async () => {
|
|
576
|
+
const expected = 'classA';
|
|
577
|
+
const output = 'Sample output';
|
|
578
|
+
const threshold = 0.5;
|
|
579
|
+
const grader = new TestGrader();
|
|
580
|
+
const grading = {
|
|
581
|
+
provider: grader,
|
|
582
|
+
};
|
|
583
|
+
await expect((0, matchers_1.matchesClassification)(expected, output, threshold, grading)).resolves.toEqual({
|
|
584
|
+
pass: true,
|
|
585
|
+
reason: `Classification ${expected} has score 0.60 >= ${threshold}`,
|
|
586
|
+
score: 0.6,
|
|
587
|
+
});
|
|
588
|
+
});
|
|
589
|
+
it('should fail when the classification score is below the threshold', async () => {
|
|
590
|
+
const expected = 'classA';
|
|
591
|
+
const output = 'Different output';
|
|
592
|
+
const threshold = 0.9;
|
|
593
|
+
const grader = new TestGrader();
|
|
594
|
+
const grading = {
|
|
595
|
+
provider: grader,
|
|
596
|
+
};
|
|
597
|
+
await expect((0, matchers_1.matchesClassification)(expected, output, threshold, grading)).resolves.toEqual({
|
|
598
|
+
pass: false,
|
|
599
|
+
reason: `Classification ${expected} has score 0.60 < ${threshold}`,
|
|
600
|
+
score: 0.6,
|
|
601
|
+
});
|
|
602
|
+
});
|
|
603
|
+
it('should pass when the maximum classification score is above the threshold with undefined expected', async () => {
|
|
604
|
+
const expected = undefined;
|
|
605
|
+
const output = 'Sample output';
|
|
606
|
+
const threshold = 0.55;
|
|
607
|
+
const grader = new TestGrader();
|
|
608
|
+
const grading = {
|
|
609
|
+
provider: grader,
|
|
610
|
+
};
|
|
611
|
+
await expect((0, matchers_1.matchesClassification)(expected, output, threshold, grading)).resolves.toEqual({
|
|
612
|
+
pass: true,
|
|
613
|
+
reason: `Maximum classification score 0.60 >= ${threshold}`,
|
|
614
|
+
score: 0.6,
|
|
615
|
+
});
|
|
616
|
+
});
|
|
617
|
+
it('should use the overridden classification grading config', async () => {
|
|
618
|
+
const expected = 'classA';
|
|
619
|
+
const output = 'Sample output';
|
|
620
|
+
const threshold = 0.5;
|
|
621
|
+
const grading = {
|
|
622
|
+
provider: {
|
|
623
|
+
id: 'hf:text-classification:foobar',
|
|
624
|
+
},
|
|
625
|
+
};
|
|
626
|
+
const mockCallApi = jest.spyOn(huggingface_1.HuggingfaceTextClassificationProvider.prototype, 'callClassificationApi');
|
|
627
|
+
mockCallApi.mockImplementation(function () {
|
|
628
|
+
return Promise.resolve({
|
|
629
|
+
classification: { [expected]: 0.6 },
|
|
630
|
+
});
|
|
631
|
+
});
|
|
632
|
+
await expect((0, matchers_1.matchesClassification)(expected, output, threshold, grading)).resolves.toEqual({
|
|
633
|
+
pass: true,
|
|
634
|
+
reason: `Classification ${expected} has score 0.60 >= ${threshold}`,
|
|
635
|
+
score: 0.6,
|
|
636
|
+
});
|
|
637
|
+
expect(mockCallApi).toHaveBeenCalledWith('Sample output');
|
|
638
|
+
mockCallApi.mockRestore();
|
|
639
|
+
});
|
|
640
|
+
});
|
|
641
|
+
describe('matchesContextRelevance', () => {
|
|
642
|
+
it('should pass when the relevance score is above the threshold', async () => {
|
|
643
|
+
const input = 'Input text';
|
|
644
|
+
const context = 'Context text';
|
|
645
|
+
const threshold = 0.5;
|
|
646
|
+
const mockCallApi = jest.spyOn(openai_2.DefaultGradingProvider, 'callApi');
|
|
647
|
+
mockCallApi.mockImplementation(() => {
|
|
648
|
+
return Promise.resolve({
|
|
649
|
+
output: 'foo\nbar\nbaz Insufficient Information',
|
|
650
|
+
tokenUsage: { total: 10, prompt: 5, completion: 5 },
|
|
651
|
+
});
|
|
652
|
+
});
|
|
653
|
+
await expect((0, matchers_2.matchesContextRelevance)(input, context, threshold)).resolves.toEqual({
|
|
654
|
+
pass: true,
|
|
655
|
+
reason: 'Relevance 0.67 is >= 0.5',
|
|
656
|
+
score: expect.closeTo(0.67, 0.01),
|
|
657
|
+
tokensUsed: {
|
|
658
|
+
total: expect.any(Number),
|
|
659
|
+
prompt: expect.any(Number),
|
|
660
|
+
completion: expect.any(Number),
|
|
661
|
+
},
|
|
662
|
+
});
|
|
663
|
+
expect(mockCallApi).toHaveBeenCalledWith(expect.stringContaining(prompts_1.CONTEXT_RELEVANCE.slice(0, 100)));
|
|
664
|
+
mockCallApi.mockRestore();
|
|
665
|
+
});
|
|
666
|
+
it('should fail when the relevance score is below the threshold', async () => {
|
|
667
|
+
const input = 'Input text';
|
|
668
|
+
const context = 'Context text';
|
|
669
|
+
const threshold = 0.9;
|
|
670
|
+
const mockCallApi = jest.spyOn(openai_2.DefaultGradingProvider, 'callApi');
|
|
671
|
+
mockCallApi.mockImplementation(() => {
|
|
672
|
+
return Promise.resolve({
|
|
673
|
+
output: 'foo\nbar\nbaz Insufficient Information',
|
|
674
|
+
tokenUsage: { total: 10, prompt: 5, completion: 5 },
|
|
675
|
+
});
|
|
676
|
+
});
|
|
677
|
+
await expect((0, matchers_2.matchesContextRelevance)(input, context, threshold)).resolves.toEqual({
|
|
678
|
+
pass: false,
|
|
679
|
+
reason: 'Relevance 0.67 is < 0.9',
|
|
680
|
+
score: expect.closeTo(0.67, 0.01),
|
|
681
|
+
tokensUsed: {
|
|
682
|
+
total: expect.any(Number),
|
|
683
|
+
prompt: expect.any(Number),
|
|
684
|
+
completion: expect.any(Number),
|
|
685
|
+
},
|
|
686
|
+
});
|
|
687
|
+
expect(mockCallApi).toHaveBeenCalledWith(expect.stringContaining(prompts_1.CONTEXT_RELEVANCE.slice(0, 100)));
|
|
688
|
+
mockCallApi.mockRestore();
|
|
689
|
+
});
|
|
690
|
+
});
|
|
691
|
+
describe('matchesContextFaithfulness', () => {
|
|
692
|
+
it('should pass when the faithfulness score is above the threshold', async () => {
|
|
693
|
+
const query = 'Query text';
|
|
694
|
+
const output = 'Output text';
|
|
695
|
+
const context = 'Context text';
|
|
696
|
+
const threshold = 0.5;
|
|
697
|
+
const mockCallApi = jest.spyOn(openai_2.DefaultGradingProvider, 'callApi');
|
|
698
|
+
mockCallApi
|
|
699
|
+
.mockImplementationOnce(() => {
|
|
700
|
+
return Promise.resolve({
|
|
701
|
+
output: 'Statement 1\nStatement 2\nStatement 3',
|
|
702
|
+
tokenUsage: { total: 10, prompt: 5, completion: 5 },
|
|
703
|
+
});
|
|
704
|
+
})
|
|
705
|
+
.mockImplementationOnce(() => {
|
|
706
|
+
return Promise.resolve({
|
|
707
|
+
output: 'Final verdict for each statement in order: Yes. No. Yes.',
|
|
708
|
+
tokenUsage: { total: 10, prompt: 5, completion: 5 },
|
|
709
|
+
});
|
|
710
|
+
});
|
|
711
|
+
await expect((0, matchers_2.matchesContextFaithfulness)(query, output, context, threshold)).resolves.toEqual({
|
|
712
|
+
pass: true,
|
|
713
|
+
reason: 'Faithfulness 0.67 is >= 0.5',
|
|
714
|
+
score: expect.closeTo(0.67, 0.01),
|
|
715
|
+
tokensUsed: {
|
|
716
|
+
total: expect.any(Number),
|
|
717
|
+
prompt: expect.any(Number),
|
|
718
|
+
completion: expect.any(Number),
|
|
719
|
+
},
|
|
720
|
+
});
|
|
721
|
+
expect(mockCallApi).toHaveBeenCalledTimes(2);
|
|
722
|
+
mockCallApi.mockRestore();
|
|
723
|
+
});
|
|
724
|
+
it('should fail when the faithfulness score is below the threshold', async () => {
|
|
725
|
+
const query = 'Query text';
|
|
726
|
+
const output = 'Output text';
|
|
727
|
+
const context = 'Context text';
|
|
728
|
+
const threshold = 0.7;
|
|
729
|
+
const mockCallApi = jest.spyOn(openai_2.DefaultGradingProvider, 'callApi');
|
|
730
|
+
mockCallApi
|
|
731
|
+
.mockImplementationOnce(() => {
|
|
732
|
+
return Promise.resolve({
|
|
733
|
+
output: 'Statement 1\nStatement 2\nStatement 3',
|
|
734
|
+
tokenUsage: { total: 10, prompt: 5, completion: 5 },
|
|
735
|
+
});
|
|
736
|
+
})
|
|
737
|
+
.mockImplementationOnce(() => {
|
|
738
|
+
return Promise.resolve({
|
|
739
|
+
output: 'Final verdict for each statement in order: Yes. Yes. No.',
|
|
740
|
+
tokenUsage: { total: 10, prompt: 5, completion: 5 },
|
|
741
|
+
});
|
|
742
|
+
});
|
|
743
|
+
await expect((0, matchers_2.matchesContextFaithfulness)(query, output, context, threshold)).resolves.toEqual({
|
|
744
|
+
pass: false,
|
|
745
|
+
reason: 'Faithfulness 0.67 is < 0.7',
|
|
746
|
+
score: expect.closeTo(0.67, 0.01),
|
|
747
|
+
tokensUsed: {
|
|
748
|
+
total: expect.any(Number),
|
|
749
|
+
prompt: expect.any(Number),
|
|
750
|
+
completion: expect.any(Number),
|
|
751
|
+
},
|
|
752
|
+
});
|
|
753
|
+
expect(mockCallApi).toHaveBeenCalledTimes(2);
|
|
754
|
+
mockCallApi.mockRestore();
|
|
755
|
+
});
|
|
756
|
+
});
|
|
757
|
+
describe('matchesContextRecall', () => {
|
|
758
|
+
it('should pass when the recall score is above the threshold', async () => {
|
|
759
|
+
const context = 'Context text';
|
|
760
|
+
const groundTruth = 'Ground truth text';
|
|
761
|
+
const threshold = 0.5;
|
|
762
|
+
const mockCallApi = jest.spyOn(openai_2.DefaultGradingProvider, 'callApi');
|
|
763
|
+
mockCallApi.mockImplementation(() => {
|
|
764
|
+
return Promise.resolve({
|
|
765
|
+
output: 'foo [Attributed]\nbar [Not attributed]\nbaz [Attributed]',
|
|
766
|
+
tokenUsage: { total: 10, prompt: 5, completion: 5 },
|
|
767
|
+
});
|
|
768
|
+
});
|
|
769
|
+
await expect((0, matchers_2.matchesContextRecall)(context, groundTruth, threshold)).resolves.toEqual({
|
|
770
|
+
pass: true,
|
|
771
|
+
reason: 'Recall 0.67 is >= 0.5',
|
|
772
|
+
score: expect.closeTo(0.67, 0.01),
|
|
773
|
+
tokensUsed: {
|
|
774
|
+
total: expect.any(Number),
|
|
775
|
+
prompt: expect.any(Number),
|
|
776
|
+
completion: expect.any(Number),
|
|
777
|
+
},
|
|
778
|
+
});
|
|
779
|
+
expect(mockCallApi).toHaveBeenCalledWith(expect.stringContaining(prompts_1.CONTEXT_RECALL.slice(0, 100)));
|
|
780
|
+
mockCallApi.mockRestore();
|
|
781
|
+
});
|
|
782
|
+
it('should fail when the recall score is below the threshold', async () => {
|
|
783
|
+
const context = 'Context text';
|
|
784
|
+
const groundTruth = 'Ground truth text';
|
|
785
|
+
const threshold = 0.9;
|
|
786
|
+
const mockCallApi = jest.spyOn(openai_2.DefaultGradingProvider, 'callApi');
|
|
787
|
+
mockCallApi.mockImplementation(() => {
|
|
788
|
+
return Promise.resolve({
|
|
789
|
+
output: 'foo [Attributed]\nbar [Not attributed]\nbaz [Attributed]',
|
|
790
|
+
tokenUsage: { total: 10, prompt: 5, completion: 5 },
|
|
791
|
+
});
|
|
792
|
+
});
|
|
793
|
+
await expect((0, matchers_2.matchesContextRecall)(context, groundTruth, threshold)).resolves.toEqual({
|
|
794
|
+
pass: false,
|
|
795
|
+
reason: 'Recall 0.67 is < 0.9',
|
|
796
|
+
score: expect.closeTo(0.67, 0.01),
|
|
797
|
+
tokensUsed: {
|
|
798
|
+
total: expect.any(Number),
|
|
799
|
+
prompt: expect.any(Number),
|
|
800
|
+
completion: expect.any(Number),
|
|
801
|
+
},
|
|
802
|
+
});
|
|
803
|
+
expect(mockCallApi).toHaveBeenCalledWith(expect.stringContaining(prompts_1.CONTEXT_RECALL.slice(0, 100)));
|
|
804
|
+
mockCallApi.mockRestore();
|
|
805
|
+
});
|
|
806
|
+
});
|
|
807
|
+
//# sourceMappingURL=matchers.test.js.map
|