@far-world-labs/verblets 0.1.1 → 0.1.2
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/.cursor/launch.json +30 -0
- package/.cursor/settings.json +20 -0
- package/.github/workflows/branch-protection.yml +22 -0
- package/.github/workflows/ci.yml +117 -0
- package/.prettierrc +6 -0
- package/.release-it.json +4 -1
- package/.vscode/launch.json +31 -0
- package/AGENTS.md +220 -0
- package/DEVELOPING.md +105 -0
- package/README.md +671 -0
- package/eslint.config.js +80 -0
- package/package.json +28 -16
- package/scripts/generate-test/index.js +29 -3
- package/scripts/runner/index.js +26 -0
- package/scripts/simple-editor/index.js +29 -18
- package/scripts/summarize-files/index.js +28 -4
- package/src/chains/README.md +30 -0
- package/src/chains/anonymize/README.md +21 -0
- package/src/chains/anonymize/index.examples.js +75 -0
- package/src/chains/anonymize/index.js +121 -0
- package/src/chains/anonymize/index.spec.js +78 -0
- package/src/chains/bulk-central-tendency/index.examples.js +138 -0
- package/src/chains/bulk-central-tendency/index.js +91 -0
- package/src/chains/bulk-filter/README.md +21 -0
- package/src/chains/bulk-filter/index.examples.js +22 -0
- package/src/chains/bulk-filter/index.js +58 -0
- package/src/chains/bulk-filter/index.spec.js +38 -0
- package/src/chains/bulk-find/README.md +16 -0
- package/src/chains/bulk-find/index.examples.js +20 -0
- package/src/chains/bulk-find/index.js +30 -0
- package/src/chains/bulk-find/index.spec.js +26 -0
- package/src/chains/bulk-group/README.md +23 -0
- package/src/chains/bulk-group/index.examples.js +18 -0
- package/src/chains/bulk-group/index.js +34 -0
- package/src/chains/bulk-group/index.spec.js +41 -0
- package/src/chains/bulk-map/README.md +43 -0
- package/src/chains/bulk-map/index.examples.js +17 -0
- package/src/chains/bulk-map/index.js +86 -0
- package/src/chains/bulk-map/index.spec.js +44 -0
- package/src/chains/bulk-reduce/README.md +12 -0
- package/src/chains/bulk-reduce/index.examples.js +15 -0
- package/src/chains/bulk-reduce/index.js +13 -0
- package/src/chains/bulk-reduce/index.spec.js +25 -0
- package/src/chains/bulk-score/README.md +16 -0
- package/src/chains/bulk-score/bulk-score-result.json +18 -0
- package/src/chains/bulk-score/index.examples.js +22 -0
- package/src/chains/bulk-score/index.js +133 -0
- package/src/chains/bulk-score/index.spec.js +30 -0
- package/src/chains/category-samples/README.md +61 -0
- package/src/chains/category-samples/index.examples.js +103 -0
- package/src/chains/category-samples/index.js +134 -0
- package/src/chains/collect-terms/README.md +12 -0
- package/src/chains/collect-terms/index.examples.js +16 -0
- package/src/chains/collect-terms/index.js +44 -0
- package/src/chains/collect-terms/index.spec.js +25 -0
- package/src/chains/date/README.md +12 -0
- package/src/chains/date/index.examples.js +47 -0
- package/src/chains/date/index.js +74 -0
- package/src/chains/date/index.spec.js +62 -0
- package/src/chains/disambiguate/README.md +22 -0
- package/src/chains/disambiguate/disambiguate-meanings-result.json +16 -0
- package/src/chains/disambiguate/index.examples.js +18 -0
- package/src/chains/disambiguate/index.js +92 -0
- package/src/chains/disambiguate/index.spec.js +25 -0
- package/src/chains/dismantle/README.md +67 -0
- package/src/chains/dismantle/dismantle.examples.js +27 -0
- package/src/chains/dismantle/index.js +6 -17
- package/src/chains/dismantle/index.spec.js +1 -2
- package/src/chains/expect/README.md +171 -0
- package/src/chains/expect/index.examples.js +146 -0
- package/src/chains/expect/index.js +173 -0
- package/src/chains/expect/index.spec.js +324 -0
- package/src/chains/filter-ambiguous/README.md +11 -0
- package/src/chains/filter-ambiguous/index.examples.js +20 -0
- package/src/chains/filter-ambiguous/index.js +49 -0
- package/src/chains/filter-ambiguous/index.spec.js +31 -0
- package/src/chains/glossary/README.md +19 -0
- package/src/chains/glossary/index.examples.js +386 -0
- package/src/chains/glossary/index.js +75 -0
- package/src/chains/glossary/index.spec.js +19 -0
- package/src/chains/intersections/README.md +152 -0
- package/src/chains/intersections/index.examples.js +279 -0
- package/src/chains/intersections/index.js +366 -0
- package/src/chains/intersections/intersection-result.json +38 -0
- package/src/chains/list/index.examples.js +12 -16
- package/src/chains/list/index.js +106 -53
- package/src/chains/list/index.spec.js +3 -9
- package/src/chains/list/list-result.json +16 -0
- package/src/chains/llm-logger/README.md +208 -0
- package/src/chains/llm-logger/index.js +205 -0
- package/src/chains/llm-logger/index.spec.js +330 -0
- package/src/chains/questions/index.examples.js +2 -1
- package/src/chains/questions/index.js +14 -15
- package/src/chains/scan-js/index.js +6 -9
- package/src/chains/set-interval/README.md +81 -0
- package/src/chains/set-interval/index.examples.js +36 -0
- package/src/chains/set-interval/index.js +131 -0
- package/src/chains/set-interval/index.spec.js +70 -0
- package/src/chains/socratic/README.md +17 -0
- package/src/chains/socratic/index.js +64 -0
- package/src/chains/socratic/index.spec.js +24 -0
- package/src/chains/sort/index.examples.js +3 -7
- package/src/chains/sort/index.js +65 -15
- package/src/chains/sort/index.spec.js +5 -8
- package/src/chains/sort/sort-result.json +16 -0
- package/src/chains/summary-map/README.md +9 -1
- package/src/chains/summary-map/index.examples.js +9 -2
- package/src/chains/summary-map/index.js +43 -25
- package/src/chains/summary-map/index.spec.js +78 -3
- package/src/chains/test/index.js +9 -13
- package/src/chains/test-advice/index.js +4 -5
- package/src/chains/themes/README.md +20 -0
- package/src/chains/themes/index.examples.js +17 -0
- package/src/chains/themes/index.js +28 -0
- package/src/chains/themes/index.spec.js +19 -0
- package/src/chains/veiled-variants/index.examples.js +18 -0
- package/src/chains/veiled-variants/index.js +107 -0
- package/src/chains/veiled-variants/index.spec.js +40 -0
- package/src/constants/common.js +0 -2
- package/src/constants/models.js +172 -0
- package/src/index.js +178 -18
- package/src/json-schemas/README.md +13 -0
- package/src/json-schemas/index.js +8 -14
- package/src/json-schemas/schema-dot-org-photograph.json +11 -5
- package/src/json-schemas/schema-dot-org-place.json +78 -5
- package/src/lib/README.md +26 -0
- package/src/lib/bulk-filter/README.md +22 -0
- package/src/lib/bulk-filter/index.examples.js +27 -0
- package/src/lib/bulk-filter/index.js +63 -0
- package/src/lib/bulk-filter/index.spec.js +38 -0
- package/src/lib/bulk-find/README.md +18 -0
- package/src/lib/bulk-find/index.examples.js +19 -0
- package/src/lib/bulk-find/index.js +30 -0
- package/src/lib/bulk-find/index.spec.js +41 -0
- package/src/lib/chatgpt/index.js +63 -43
- package/src/lib/combinations/index.js +30 -0
- package/src/lib/combinations/index.spec.js +23 -0
- package/src/lib/functional/index.js +28 -0
- package/src/lib/logger-service/index.js +32 -0
- package/src/lib/parse-js-parts/index.js +9 -21
- package/src/lib/parse-llm-list/README.md +39 -0
- package/src/lib/parse-llm-list/index.js +54 -0
- package/src/lib/parse-llm-list/index.spec.js +59 -0
- package/src/lib/path-aliases/index.js +1 -3
- package/src/lib/path-aliases/index.spec.js +2 -8
- package/src/lib/pave/index.js +4 -4
- package/src/lib/pave/index.spec.js +6 -3
- package/src/lib/prompt-cache/index.js +14 -10
- package/src/lib/retry/index.js +11 -8
- package/src/lib/ring-buffer/README.md +460 -0
- package/src/lib/ring-buffer/index.js +1074 -0
- package/src/lib/search-best-first/city-walk.spec.js +37 -0
- package/src/lib/search-best-first/index.js +42 -11
- package/src/lib/search-best-first/index.spec.js +35 -0
- package/src/lib/search-js-files/index.js +21 -41
- package/src/lib/search-js-files/scan-file.js +10 -21
- package/src/lib/shorten-text/index.js +2 -7
- package/src/lib/shorten-text/index.spec.js +3 -3
- package/src/lib/strip-response/index.js +2 -7
- package/src/lib/template-replace/index.js +23 -0
- package/src/lib/template-replace/index.spec.js +60 -0
- package/src/lib/to-date/index.js +11 -0
- package/src/lib/to-number/index.js +1 -1
- package/src/lib/transcribe/index.js +4 -4
- package/src/prompts/README.md +3 -1
- package/src/prompts/as-object-with-schema.js +3 -8
- package/src/prompts/as-schema-org-text.js +10 -2
- package/src/prompts/code-features.js +1 -5
- package/src/prompts/constants.js +27 -27
- package/src/prompts/generate-collection.js +1 -1
- package/src/prompts/intent.js +11 -16
- package/src/prompts/select-from-threshold.js +1 -2
- package/src/prompts/sort.js +4 -8
- package/src/prompts/style.js +4 -7
- package/src/prompts/wrap-list.js +1 -4
- package/src/services/llm-model/global-overrides.spec.js +432 -0
- package/src/services/llm-model/index.js +234 -40
- package/src/services/llm-model/model.js +2 -2
- package/src/services/llm-model/negotiate.spec.js +447 -0
- package/src/services/redis/index.js +70 -7
- package/src/test/setup.js +20 -0
- package/src/verblets/README.md +26 -0
- package/src/verblets/auto/index.examples.js +12 -9
- package/src/verblets/auto/index.js +10 -10
- package/src/verblets/auto/index.spec.js +4 -6
- package/src/verblets/bool/README.md +36 -0
- package/src/verblets/bool/index.examples.js +53 -1
- package/src/verblets/bool/index.js +6 -9
- package/src/verblets/bool/index.spec.js +1 -3
- package/src/verblets/central-tendency/README.md +166 -0
- package/src/verblets/central-tendency/central-tendency-result.json +24 -0
- package/src/verblets/central-tendency/index.examples.js +196 -0
- package/src/verblets/central-tendency/index.js +171 -0
- package/src/verblets/central-tendency/index.spec.js +148 -0
- package/src/verblets/enum/index.examples.js +1 -4
- package/src/verblets/enum/index.js +7 -4
- package/src/verblets/expect/README.md +64 -0
- package/src/verblets/expect/index.examples.js +109 -0
- package/src/verblets/expect/index.js +75 -0
- package/src/verblets/expect/index.spec.js +127 -0
- package/src/verblets/intent/index.examples.js +84 -1
- package/src/verblets/intent/index.js +56 -68
- package/src/verblets/intersection/README.md +16 -0
- package/src/verblets/intersection/index.examples.js +89 -0
- package/src/verblets/intersection/index.js +84 -0
- package/src/verblets/intersection/index.spec.js +60 -0
- package/src/verblets/intersection/intersection-result.json +16 -0
- package/src/verblets/list-expand/README.md +10 -0
- package/src/verblets/list-expand/index.examples.js +14 -0
- package/src/verblets/list-expand/index.js +104 -0
- package/src/verblets/list-expand/index.spec.js +18 -0
- package/src/verblets/list-expand/list-expand-result.json +16 -0
- package/src/verblets/list-filter/README.md +22 -0
- package/src/verblets/list-filter/index.examples.js +26 -0
- package/src/verblets/list-filter/index.js +18 -0
- package/src/verblets/list-filter/index.spec.js +19 -0
- package/src/verblets/list-find/README.md +11 -0
- package/src/verblets/list-find/index.examples.js +15 -0
- package/src/verblets/list-find/index.js +17 -0
- package/src/verblets/list-find/index.spec.js +19 -0
- package/src/verblets/list-group/README.md +16 -0
- package/src/verblets/list-group/index.examples.js +16 -0
- package/src/verblets/list-group/index.js +112 -0
- package/src/verblets/list-group/index.spec.js +35 -0
- package/src/verblets/list-group/list-group-result.json +16 -0
- package/src/verblets/list-map/README.md +11 -0
- package/src/verblets/list-map/index.examples.js +15 -0
- package/src/verblets/list-map/index.js +26 -0
- package/src/verblets/list-map/index.spec.js +17 -0
- package/src/verblets/list-reduce/README.md +10 -0
- package/src/verblets/list-reduce/index.examples.js +14 -0
- package/src/verblets/list-reduce/index.js +21 -0
- package/src/verblets/list-reduce/index.spec.js +27 -0
- package/src/verblets/list-reduce/index.spec.jsx +27 -0
- package/src/verblets/name/README.md +15 -0
- package/src/verblets/name/index.examples.js +28 -0
- package/src/verblets/name/index.js +19 -0
- package/src/verblets/name/index.spec.js +33 -0
- package/src/verblets/name-similar-to/README.md +26 -0
- package/src/verblets/name-similar-to/index.examples.js +18 -0
- package/src/verblets/name-similar-to/index.js +20 -0
- package/src/verblets/name-similar-to/index.spec.js +13 -0
- package/src/verblets/number/index.examples.js +173 -7
- package/src/verblets/number/index.js +5 -2
- package/src/verblets/number/index.spec.js +1 -3
- package/src/verblets/number-with-units/index.examples.js +5 -1
- package/src/verblets/number-with-units/index.js +74 -9
- package/src/verblets/number-with-units/number-with-units-result.json +23 -0
- package/src/verblets/schema-org/index.examples.js +2 -7
- package/src/verblets/schema-org/index.js +32 -3
- package/src/verblets/sentiment/README.md +10 -0
- package/src/verblets/sentiment/index.examples.js +20 -0
- package/src/verblets/sentiment/index.js +9 -0
- package/src/verblets/sentiment/index.spec.js +20 -0
- package/src/verblets/to-object/index.js +10 -15
- package/src/verblets/to-object/index.spec.js +1 -4
- package/.eslintrc.json +0 -42
- package/docs/README.md +0 -41
- package/docs/babel.config.js +0 -3
- package/docs/blog/2019-05-28-first-blog-post.md +0 -12
- package/docs/blog/2019-05-29-long-blog-post.md +0 -44
- package/docs/blog/2021-08-01-mdx-blog-post.mdx +0 -20
- package/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg +0 -0
- package/docs/blog/2021-08-26-welcome/index.md +0 -25
- package/docs/blog/authors.yml +0 -17
- package/docs/docs/api/bool.md +0 -74
- package/docs/docs/api/search.md +0 -51
- package/docs/docs/intro.md +0 -47
- package/docs/docs/tutorial-basics/_category_.json +0 -8
- package/docs/docs/tutorial-basics/congratulations.md +0 -23
- package/docs/docs/tutorial-basics/create-a-blog-post.md +0 -34
- package/docs/docs/tutorial-basics/create-a-document.md +0 -57
- package/docs/docs/tutorial-basics/create-a-page.md +0 -43
- package/docs/docs/tutorial-basics/deploy-your-site.md +0 -31
- package/docs/docs/tutorial-basics/markdown-features.mdx +0 -152
- package/docs/docs/tutorial-extras/_category_.json +0 -7
- package/docs/docs/tutorial-extras/img/docsVersionDropdown.png +0 -0
- package/docs/docs/tutorial-extras/img/localeDropdown.png +0 -0
- package/docs/docs/tutorial-extras/manage-docs-versions.md +0 -55
- package/docs/docs/tutorial-extras/translate-your-site.md +0 -88
- package/docs/docusaurus.config.js +0 -120
- package/docs/package.json +0 -44
- package/docs/sidebars.js +0 -31
- package/docs/src/components/HomepageFeatures/index.js +0 -61
- package/docs/src/components/HomepageFeatures/styles.module.css +0 -11
- package/docs/src/css/custom.css +0 -30
- package/docs/src/pages/index.js +0 -43
- package/docs/src/pages/index.module.css +0 -23
- package/docs/src/pages/markdown-page.md +0 -7
- package/docs/static/.nojekyll +0 -0
- package/docs/static/img/docusaurus-social-card.jpg +0 -0
- package/docs/static/img/docusaurus.png +0 -0
- package/docs/static/img/favicon.ico +0 -0
- package/docs/static/img/logo.svg +0 -1
- package/docs/static/img/undraw_docusaurus_mountain.svg +0 -171
- package/docs/static/img/undraw_docusaurus_react.svg +0 -170
- package/docs/static/img/undraw_docusaurus_tree.svg +0 -40
- package/src/constants/openai.js +0 -65
- /package/{.vite.config.examples.js → .vitest.config.examples.js} +0 -0
- /package/{.vite.config.js → .vitest.config.js} +0 -0
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import { describe, expect as vitestExpect, it, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import aiExpectSimple, { expect as aiExpect } from './index.js';
|
|
3
|
+
import { longTestTimeout } from '../../constants/common.js';
|
|
4
|
+
|
|
5
|
+
// Mock the chatgpt function to avoid actual API calls
|
|
6
|
+
vi.mock('../../lib/chatgpt/index.js', () => ({
|
|
7
|
+
default: vi.fn().mockImplementation((prompt) => {
|
|
8
|
+
// Handle exact equality checks
|
|
9
|
+
if (prompt.includes('Does the actual value strictly equal the expected value?')) {
|
|
10
|
+
if (prompt.includes('Actual: "hello"') && prompt.includes('Expected: "hello"')) {
|
|
11
|
+
return 'True';
|
|
12
|
+
}
|
|
13
|
+
if (prompt.includes('Actual: "goodbye"') && prompt.includes('Expected: "hello"')) {
|
|
14
|
+
return 'False';
|
|
15
|
+
}
|
|
16
|
+
if (prompt.includes('Actual: "hello"') && prompt.includes('Expected: "goodbye"')) {
|
|
17
|
+
return 'False';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Handle constraint-based validations (format: "Given this constraint:")
|
|
22
|
+
if (prompt.includes('Given this constraint:')) {
|
|
23
|
+
if (prompt.includes('Is this a greeting?') && prompt.includes('Hello world!')) {
|
|
24
|
+
return 'True';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (prompt.includes('Is this text professional and grammatically correct?')) {
|
|
28
|
+
if (prompt.includes('well-written, professional email')) {
|
|
29
|
+
return 'True';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (prompt.includes('Does this person data look realistic?')) {
|
|
34
|
+
if (prompt.includes('John Doe') && prompt.includes('"age": 30')) {
|
|
35
|
+
return 'True';
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (prompt.includes('Is this recommendation specific and actionable?')) {
|
|
40
|
+
if (prompt.includes('Increase marketing budget by 20%')) {
|
|
41
|
+
return 'True';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (prompt.includes('Does this profile represent an experienced software developer')) {
|
|
46
|
+
if (prompt.includes('Alice Johnson') && prompt.includes('JavaScript')) {
|
|
47
|
+
return 'True';
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (prompt.includes('Is this story opening engaging')) {
|
|
52
|
+
if (prompt.includes('Once upon a time')) {
|
|
53
|
+
return 'True';
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Default to False for unmatched cases
|
|
59
|
+
return 'False';
|
|
60
|
+
}),
|
|
61
|
+
}));
|
|
62
|
+
|
|
63
|
+
describe('expect chain', () => {
|
|
64
|
+
let originalEnv;
|
|
65
|
+
|
|
66
|
+
beforeEach(() => {
|
|
67
|
+
originalEnv = process.env.LLM_EXPECT_MODE;
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
afterEach(() => {
|
|
71
|
+
if (originalEnv !== undefined) {
|
|
72
|
+
process.env.LLM_EXPECT_MODE = originalEnv;
|
|
73
|
+
} else {
|
|
74
|
+
delete process.env.LLM_EXPECT_MODE;
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe('Enhanced API', () => {
|
|
79
|
+
it(
|
|
80
|
+
'should return structured results in none mode',
|
|
81
|
+
async () => {
|
|
82
|
+
process.env.LLM_EXPECT_MODE = 'none';
|
|
83
|
+
|
|
84
|
+
const [passed, details] = await aiExpect('hello', 'hello');
|
|
85
|
+
|
|
86
|
+
vitestExpect(passed).toBe(true);
|
|
87
|
+
vitestExpect(details).toHaveProperty('passed', true);
|
|
88
|
+
vitestExpect(details).toHaveProperty('advice');
|
|
89
|
+
vitestExpect(details).toHaveProperty('file');
|
|
90
|
+
vitestExpect(details).toHaveProperty('line');
|
|
91
|
+
},
|
|
92
|
+
longTestTimeout
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
it(
|
|
96
|
+
'should handle failed assertions in none mode',
|
|
97
|
+
async () => {
|
|
98
|
+
process.env.LLM_EXPECT_MODE = 'none';
|
|
99
|
+
|
|
100
|
+
const [passed, details] = await aiExpect('hello', 'goodbye');
|
|
101
|
+
|
|
102
|
+
vitestExpect(passed).toBe(false);
|
|
103
|
+
vitestExpect(details.passed).toBe(false);
|
|
104
|
+
vitestExpect(details).toHaveProperty('advice');
|
|
105
|
+
vitestExpect(details).toHaveProperty('file');
|
|
106
|
+
vitestExpect(details).toHaveProperty('line');
|
|
107
|
+
},
|
|
108
|
+
longTestTimeout
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
it(
|
|
112
|
+
'should throw errors in error mode',
|
|
113
|
+
async () => {
|
|
114
|
+
process.env.LLM_EXPECT_MODE = 'error';
|
|
115
|
+
|
|
116
|
+
await vitestExpect(async () => {
|
|
117
|
+
await aiExpect('hello', 'goodbye');
|
|
118
|
+
}).rejects.toThrow('LLM Assertion Failed');
|
|
119
|
+
},
|
|
120
|
+
longTestTimeout
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
it(
|
|
124
|
+
'should log in info mode',
|
|
125
|
+
async () => {
|
|
126
|
+
process.env.LLM_EXPECT_MODE = 'info';
|
|
127
|
+
const consoleSpy = vi.spyOn(console, 'info').mockImplementation(() => {});
|
|
128
|
+
|
|
129
|
+
const [passed] = await aiExpect('hello', 'goodbye');
|
|
130
|
+
|
|
131
|
+
vitestExpect(passed).toBe(false);
|
|
132
|
+
vitestExpect(consoleSpy).toHaveBeenCalledWith(
|
|
133
|
+
vitestExpect.stringContaining('LLM Assertion Failed')
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
consoleSpy.mockRestore();
|
|
137
|
+
},
|
|
138
|
+
longTestTimeout
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
it(
|
|
142
|
+
'should handle constraint-based validation',
|
|
143
|
+
async () => {
|
|
144
|
+
process.env.LLM_EXPECT_MODE = 'none';
|
|
145
|
+
|
|
146
|
+
const [passed, details] = await aiExpect('Hello world!', undefined, 'Is this a greeting?');
|
|
147
|
+
|
|
148
|
+
vitestExpect(passed).toBe(true);
|
|
149
|
+
vitestExpect(details.passed).toBe(true);
|
|
150
|
+
},
|
|
151
|
+
longTestTimeout
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
it(
|
|
155
|
+
'should validate content quality',
|
|
156
|
+
async () => {
|
|
157
|
+
const [passed, details] = await aiExpect(
|
|
158
|
+
'This is a well-written, professional email with proper grammar and clear intent.',
|
|
159
|
+
undefined,
|
|
160
|
+
'Is this text professional and grammatically correct?'
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
vitestExpect(passed).toBe(true);
|
|
164
|
+
vitestExpect(details).toHaveProperty('file');
|
|
165
|
+
vitestExpect(details).toHaveProperty('line');
|
|
166
|
+
},
|
|
167
|
+
longTestTimeout
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
it(
|
|
171
|
+
'should validate data structures',
|
|
172
|
+
async () => {
|
|
173
|
+
const [passed] = await aiExpect(
|
|
174
|
+
{ name: 'John Doe', age: 30, city: 'New York' },
|
|
175
|
+
undefined,
|
|
176
|
+
'Does this person data look realistic?'
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
vitestExpect(passed).toBe(true);
|
|
180
|
+
},
|
|
181
|
+
longTestTimeout
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
it(
|
|
185
|
+
'should handle business logic validation',
|
|
186
|
+
async () => {
|
|
187
|
+
const [passed] = await aiExpect(
|
|
188
|
+
'Increase marketing budget by 20% for Q4 to boost holiday sales',
|
|
189
|
+
undefined,
|
|
190
|
+
'Is this recommendation specific and actionable?'
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
vitestExpect(passed).toBe(true);
|
|
194
|
+
},
|
|
195
|
+
longTestTimeout
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
it('should throw error when neither expected nor constraint provided', async () => {
|
|
199
|
+
await vitestExpect(async () => {
|
|
200
|
+
await aiExpect('test value');
|
|
201
|
+
}).rejects.toThrow('Either expected value or constraint must be provided');
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
describe('Simple API (backward compatibility)', () => {
|
|
206
|
+
it(
|
|
207
|
+
'should pass for exact equality',
|
|
208
|
+
async () => {
|
|
209
|
+
const result = await aiExpectSimple('hello', 'hello');
|
|
210
|
+
vitestExpect(result).toBe(true);
|
|
211
|
+
},
|
|
212
|
+
longTestTimeout
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
it(
|
|
216
|
+
'should pass for constraint-based validation',
|
|
217
|
+
async () => {
|
|
218
|
+
const result = await aiExpectSimple('Hello world!', undefined, 'Is this a greeting?');
|
|
219
|
+
vitestExpect(result).toBe(true);
|
|
220
|
+
},
|
|
221
|
+
longTestTimeout
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
it(
|
|
225
|
+
'should fail for non-matching values',
|
|
226
|
+
async () => {
|
|
227
|
+
const result = await aiExpectSimple('goodbye', 'hello');
|
|
228
|
+
vitestExpect(result).toBe(false);
|
|
229
|
+
},
|
|
230
|
+
longTestTimeout
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
it(
|
|
234
|
+
'should validate content quality',
|
|
235
|
+
async () => {
|
|
236
|
+
const result = await aiExpectSimple(
|
|
237
|
+
'This is a well-written, professional email with proper grammar.',
|
|
238
|
+
undefined,
|
|
239
|
+
'Is this text professional and grammatically correct?'
|
|
240
|
+
);
|
|
241
|
+
vitestExpect(result).toBe(true);
|
|
242
|
+
},
|
|
243
|
+
longTestTimeout
|
|
244
|
+
);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
describe('Environment variable handling', () => {
|
|
248
|
+
it(
|
|
249
|
+
'should default to none mode when env var is not set',
|
|
250
|
+
async () => {
|
|
251
|
+
delete process.env.LLM_EXPECT_MODE;
|
|
252
|
+
|
|
253
|
+
const [passed] = await aiExpect('hello', 'goodbye');
|
|
254
|
+
vitestExpect(passed).toBe(false);
|
|
255
|
+
// Should not throw in none mode
|
|
256
|
+
},
|
|
257
|
+
longTestTimeout
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
it(
|
|
261
|
+
'should handle invalid env var values',
|
|
262
|
+
async () => {
|
|
263
|
+
process.env.LLM_EXPECT_MODE = 'invalid';
|
|
264
|
+
|
|
265
|
+
const [passed] = await aiExpect('hello', 'goodbye');
|
|
266
|
+
vitestExpect(passed).toBe(false);
|
|
267
|
+
// Should default to none mode and not throw
|
|
268
|
+
},
|
|
269
|
+
longTestTimeout
|
|
270
|
+
);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
describe('Advanced features', () => {
|
|
274
|
+
it(
|
|
275
|
+
'should provide file and line information',
|
|
276
|
+
async () => {
|
|
277
|
+
const [, details] = await aiExpect('hello', 'hello');
|
|
278
|
+
|
|
279
|
+
vitestExpect(details.file).toBeDefined();
|
|
280
|
+
vitestExpect(details.line).toBeTypeOf('number');
|
|
281
|
+
vitestExpect(details.line).toBeGreaterThan(0);
|
|
282
|
+
},
|
|
283
|
+
longTestTimeout
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
it(
|
|
287
|
+
'should handle complex object comparisons',
|
|
288
|
+
async () => {
|
|
289
|
+
const userProfile = {
|
|
290
|
+
name: 'Alice Johnson',
|
|
291
|
+
skills: ['JavaScript', 'Python', 'React'],
|
|
292
|
+
experience: '5 years',
|
|
293
|
+
level: 'Senior Developer',
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
const [passed] = await aiExpect(
|
|
297
|
+
userProfile,
|
|
298
|
+
undefined,
|
|
299
|
+
'Does this profile represent an experienced software developer with modern skills?'
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
vitestExpect(passed).toBe(true);
|
|
303
|
+
},
|
|
304
|
+
longTestTimeout
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
it(
|
|
308
|
+
'should validate creative content',
|
|
309
|
+
async () => {
|
|
310
|
+
const storyOpening =
|
|
311
|
+
'Once upon a time, in a land far away, there lived a brave knight who embarked on a quest to save the kingdom from an ancient curse.';
|
|
312
|
+
|
|
313
|
+
const [passed] = await aiExpect(
|
|
314
|
+
storyOpening,
|
|
315
|
+
undefined,
|
|
316
|
+
'Is this story opening engaging and sets up a clear adventure narrative?'
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
vitestExpect(passed).toBe(true);
|
|
320
|
+
},
|
|
321
|
+
longTestTimeout
|
|
322
|
+
);
|
|
323
|
+
});
|
|
324
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# filter-ambiguous
|
|
2
|
+
|
|
3
|
+
Identify ambiguous words or phrases in text. Sentences are first scored for overall uncertainty and the highest scoring ones are examined for unclear terms. Each term is scored in context so you can zero in on the most confusing language.
|
|
4
|
+
|
|
5
|
+
```javascript
|
|
6
|
+
import filterAmbiguous from './index.js';
|
|
7
|
+
|
|
8
|
+
const text = `I saw her duck\nThe magician made a little girl disappear\nHe fed her dog food`;
|
|
9
|
+
const result = await filterAmbiguous(text, { topN: 3 });
|
|
10
|
+
// => [{ term: 'duck', sentence: 'I saw her duck', score: 9 }, ...]
|
|
11
|
+
```
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { longTestTimeout } from '../../constants/common.js';
|
|
3
|
+
import filterAmbiguous from './index.js';
|
|
4
|
+
|
|
5
|
+
describe('filterAmbiguous examples', () => {
|
|
6
|
+
it(
|
|
7
|
+
'highlights unclear language',
|
|
8
|
+
async () => {
|
|
9
|
+
const text = `I saw her duck\nThe magician made a little girl disappear\nHe fed her dog food`;
|
|
10
|
+
const result = await filterAmbiguous(text, { topN: 2 });
|
|
11
|
+
expect(result.length).toBeGreaterThan(0);
|
|
12
|
+
result.forEach((r) => {
|
|
13
|
+
expect(r).toHaveProperty('term');
|
|
14
|
+
expect(r).toHaveProperty('sentence');
|
|
15
|
+
expect(typeof r.score).toBe('number');
|
|
16
|
+
});
|
|
17
|
+
},
|
|
18
|
+
longTestTimeout
|
|
19
|
+
);
|
|
20
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import list from '../list/index.js';
|
|
2
|
+
import bulkScore from '../bulk-score/index.js';
|
|
3
|
+
|
|
4
|
+
export default async function filterAmbiguous(text, config = {}) {
|
|
5
|
+
const { topN = 10, chunkSize = 5, llm, ...options } = config;
|
|
6
|
+
if (!text) return [];
|
|
7
|
+
const sentences = text
|
|
8
|
+
.split('\n')
|
|
9
|
+
.map((s) => s.trim())
|
|
10
|
+
.filter(Boolean);
|
|
11
|
+
if (sentences.length === 0) return [];
|
|
12
|
+
|
|
13
|
+
const { scores: sentenceScores } = await bulkScore(
|
|
14
|
+
sentences,
|
|
15
|
+
'How ambiguous or easily misinterpreted is this sentence?',
|
|
16
|
+
{ chunkSize, llm, ...options }
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
const rankedSentences = sentences
|
|
20
|
+
.map((s, i) => ({ sentence: s, score: sentenceScores[i] ?? 0 }))
|
|
21
|
+
.sort((a, b) => b.score - a.score)
|
|
22
|
+
.slice(0, topN);
|
|
23
|
+
|
|
24
|
+
const termPairs = [];
|
|
25
|
+
for (const { sentence } of rankedSentences) {
|
|
26
|
+
// eslint-disable-next-line no-await-in-loop
|
|
27
|
+
const terms = await list('Ambiguous words or short phrases', {
|
|
28
|
+
attachments: { text: sentence },
|
|
29
|
+
targetNewItemsCount: 5,
|
|
30
|
+
llm,
|
|
31
|
+
...options,
|
|
32
|
+
});
|
|
33
|
+
terms.forEach((term) => {
|
|
34
|
+
termPairs.push({ term, sentence });
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (termPairs.length === 0) return [];
|
|
39
|
+
|
|
40
|
+
const { scores } = await bulkScore(
|
|
41
|
+
termPairs.map((p) => `${p.term} | ${p.sentence}`),
|
|
42
|
+
'Score how ambiguous the term is within the sentence.',
|
|
43
|
+
{ chunkSize, llm, ...options }
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const scored = termPairs.map((p, i) => ({ ...p, score: scores[i] }));
|
|
47
|
+
scored.sort((a, b) => (b.score || 0) - (a.score || 0));
|
|
48
|
+
return scored.slice(0, topN);
|
|
49
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import filterAmbiguous from './index.js';
|
|
3
|
+
import bulkScore from '../bulk-score/index.js';
|
|
4
|
+
import list from '../list/index.js';
|
|
5
|
+
|
|
6
|
+
vi.mock('../bulk-score/index.js', () => ({
|
|
7
|
+
default: vi.fn(),
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
vi.mock('../list/index.js', () => ({
|
|
11
|
+
default: vi.fn(),
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
vi.clearAllMocks();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe('filterAmbiguous chain', () => {
|
|
19
|
+
it('returns scored ambiguous terms', async () => {
|
|
20
|
+
bulkScore
|
|
21
|
+
.mockResolvedValueOnce({ scores: [1, 9], reference: [] })
|
|
22
|
+
.mockResolvedValueOnce({ scores: [8, 3], reference: [] });
|
|
23
|
+
list.mockResolvedValueOnce(['alpha', 'beta']).mockResolvedValueOnce([]);
|
|
24
|
+
|
|
25
|
+
const result = await filterAmbiguous('s1\ns2', { topN: 1 });
|
|
26
|
+
|
|
27
|
+
expect(result).toStrictEqual([{ term: 'alpha', sentence: 's2', score: 8 }]);
|
|
28
|
+
expect(bulkScore).toHaveBeenCalled();
|
|
29
|
+
expect(list).toHaveBeenCalled();
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# glossary
|
|
2
|
+
|
|
3
|
+
Identify difficult or technical terms in any text so you can explain them later.
|
|
4
|
+
The chain breaks long passages into paragraphs and uses the `bulk-map` retry
|
|
5
|
+
utility to collect candidate terms from each chunk. Any failed paragraphs are
|
|
6
|
+
automatically retried. Finally the terms are ranked by importance using `sort`.
|
|
7
|
+
|
|
8
|
+
```javascript
|
|
9
|
+
import glossary from './index.js';
|
|
10
|
+
|
|
11
|
+
const blog = `The chef explained how umami develops through the Maillard reaction alongside sous-vide techniques.`;
|
|
12
|
+
|
|
13
|
+
const terms = await glossary(blog, { maxTerms: 3 });
|
|
14
|
+
console.log(terms);
|
|
15
|
+
// => ['Maillard reaction', 'sous-vide', 'umami']
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
This is handy when you want to add a quick glossary sidebar to a dense article
|
|
19
|
+
so everyday readers aren't left guessing what key terms mean.
|