@far-world-labs/verblets 0.1.7 → 0.3.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/README.md +86 -213
- package/dist/index.browser.js +74 -0
- package/dist/index.js +548 -0
- package/dist/shared-C6kPWghF.js +7806 -0
- package/package.json +32 -8
- package/.cursor/launch.json +0 -30
- package/.cursor/settings.json +0 -20
- package/.github/workflows/branch-protection.yml +0 -22
- package/.github/workflows/ci.yml +0 -165
- package/.husky/pre-commit +0 -4
- package/.prettierrc +0 -6
- package/.release-it.json +0 -12
- package/.vitest.config.examples.js +0 -8
- package/.vitest.config.js +0 -8
- package/.vscode/launch.json +0 -31
- package/AGENTS.md +0 -220
- package/DEVELOPING.md +0 -105
- package/docker-compose.yml +0 -7
- package/eslint.config.js +0 -80
- package/scripts/generate-chain/index.js +0 -111
- package/scripts/generate-lib/index.js +0 -68
- package/scripts/generate-test/index.js +0 -137
- package/scripts/generate-verblet/README.md +0 -17
- package/scripts/generate-verblet/index.js +0 -110
- package/scripts/run.sh +0 -15
- package/scripts/runner/index.js +0 -56
- package/scripts/simple-editor/README.md +0 -34
- package/scripts/simple-editor/index.js +0 -79
- package/scripts/summarize-files/index.js +0 -70
- package/src/chains/README.md +0 -30
- package/src/chains/anonymize/README.md +0 -21
- package/src/chains/anonymize/index.examples.js +0 -75
- package/src/chains/anonymize/index.js +0 -121
- package/src/chains/anonymize/index.spec.js +0 -78
- package/src/chains/bulk-central-tendency/index.examples.js +0 -138
- package/src/chains/bulk-central-tendency/index.js +0 -91
- package/src/chains/bulk-filter/README.md +0 -21
- package/src/chains/bulk-filter/index.examples.js +0 -22
- package/src/chains/bulk-filter/index.js +0 -58
- package/src/chains/bulk-filter/index.spec.js +0 -38
- package/src/chains/bulk-find/README.md +0 -16
- package/src/chains/bulk-find/index.examples.js +0 -20
- package/src/chains/bulk-find/index.js +0 -30
- package/src/chains/bulk-find/index.spec.js +0 -26
- package/src/chains/bulk-group/README.md +0 -23
- package/src/chains/bulk-group/index.examples.js +0 -18
- package/src/chains/bulk-group/index.js +0 -34
- package/src/chains/bulk-group/index.spec.js +0 -41
- package/src/chains/bulk-map/README.md +0 -43
- package/src/chains/bulk-map/index.examples.js +0 -17
- package/src/chains/bulk-map/index.js +0 -86
- package/src/chains/bulk-map/index.spec.js +0 -44
- package/src/chains/bulk-reduce/README.md +0 -12
- package/src/chains/bulk-reduce/index.examples.js +0 -15
- package/src/chains/bulk-reduce/index.js +0 -13
- package/src/chains/bulk-reduce/index.spec.js +0 -25
- package/src/chains/bulk-score/README.md +0 -16
- package/src/chains/bulk-score/bulk-score-result.json +0 -18
- package/src/chains/bulk-score/index.examples.js +0 -22
- package/src/chains/bulk-score/index.js +0 -133
- package/src/chains/bulk-score/index.spec.js +0 -30
- package/src/chains/category-samples/README.md +0 -61
- package/src/chains/category-samples/index.examples.js +0 -103
- package/src/chains/category-samples/index.js +0 -134
- package/src/chains/collect-terms/README.md +0 -12
- package/src/chains/collect-terms/index.examples.js +0 -16
- package/src/chains/collect-terms/index.js +0 -44
- package/src/chains/collect-terms/index.spec.js +0 -25
- package/src/chains/date/README.md +0 -12
- package/src/chains/date/index.examples.js +0 -47
- package/src/chains/date/index.js +0 -74
- package/src/chains/date/index.spec.js +0 -62
- package/src/chains/disambiguate/README.md +0 -22
- package/src/chains/disambiguate/disambiguate-meanings-result.json +0 -16
- package/src/chains/disambiguate/index.examples.js +0 -18
- package/src/chains/disambiguate/index.js +0 -92
- package/src/chains/disambiguate/index.spec.js +0 -25
- package/src/chains/dismantle/README.md +0 -67
- package/src/chains/dismantle/dismantle.examples.js +0 -27
- package/src/chains/dismantle/index.examples.js +0 -30
- package/src/chains/dismantle/index.js +0 -303
- package/src/chains/dismantle/index.spec.js +0 -32
- package/src/chains/expect/README.md +0 -171
- package/src/chains/expect/index.examples.js +0 -146
- package/src/chains/expect/index.js +0 -173
- package/src/chains/expect/index.spec.js +0 -324
- package/src/chains/filter-ambiguous/README.md +0 -11
- package/src/chains/filter-ambiguous/index.examples.js +0 -20
- package/src/chains/filter-ambiguous/index.js +0 -49
- package/src/chains/filter-ambiguous/index.spec.js +0 -31
- package/src/chains/glossary/README.md +0 -19
- package/src/chains/glossary/index.examples.js +0 -386
- package/src/chains/glossary/index.js +0 -75
- package/src/chains/glossary/index.spec.js +0 -19
- package/src/chains/intersections/README.md +0 -152
- package/src/chains/intersections/index.examples.js +0 -279
- package/src/chains/intersections/index.js +0 -366
- package/src/chains/intersections/intersection-result.json +0 -38
- package/src/chains/list/index.examples.js +0 -68
- package/src/chains/list/index.js +0 -214
- package/src/chains/list/index.spec.js +0 -67
- package/src/chains/list/list-result.json +0 -16
- package/src/chains/list/schema.json +0 -24
- package/src/chains/llm-logger/README.md +0 -208
- package/src/chains/llm-logger/index.js +0 -205
- package/src/chains/llm-logger/index.spec.js +0 -330
- package/src/chains/questions/index.examples.js +0 -69
- package/src/chains/questions/index.js +0 -135
- package/src/chains/questions/index.spec.js +0 -29
- package/src/chains/scan-js/index.js +0 -116
- package/src/chains/set-interval/README.md +0 -81
- package/src/chains/set-interval/index.examples.js +0 -36
- package/src/chains/set-interval/index.js +0 -131
- package/src/chains/set-interval/index.spec.js +0 -70
- package/src/chains/socratic/README.md +0 -17
- package/src/chains/socratic/index.js +0 -64
- package/src/chains/socratic/index.spec.js +0 -24
- package/src/chains/sort/index.examples.js +0 -36
- package/src/chains/sort/index.js +0 -163
- package/src/chains/sort/index.spec.js +0 -112
- package/src/chains/sort/sort-result.json +0 -16
- package/src/chains/summary-map/README.md +0 -41
- package/src/chains/summary-map/index.examples.js +0 -64
- package/src/chains/summary-map/index.js +0 -226
- package/src/chains/summary-map/index.spec.js +0 -153
- package/src/chains/test/index.js +0 -114
- package/src/chains/test-advice/index.js +0 -35
- package/src/chains/themes/README.md +0 -20
- package/src/chains/themes/index.examples.js +0 -17
- package/src/chains/themes/index.js +0 -28
- package/src/chains/themes/index.spec.js +0 -19
- package/src/chains/veiled-variants/index.examples.js +0 -18
- package/src/chains/veiled-variants/index.js +0 -107
- package/src/chains/veiled-variants/index.spec.js +0 -40
- package/src/constants/common.js +0 -7
- package/src/constants/messages.js +0 -3
- package/src/constants/models.js +0 -183
- package/src/index.js +0 -193
- package/src/json-schemas/README.md +0 -13
- package/src/json-schemas/cars-test.json +0 -11
- package/src/json-schemas/index.js +0 -12
- package/src/json-schemas/intent.json +0 -38
- package/src/json-schemas/schema-dot-org-photograph.json +0 -133
- package/src/json-schemas/schema-dot-org-place.json +0 -129
- package/src/lib/README.md +0 -26
- package/src/lib/any-signal/index.js +0 -28
- package/src/lib/bulk-filter/README.md +0 -22
- package/src/lib/bulk-filter/index.examples.js +0 -27
- package/src/lib/bulk-filter/index.js +0 -63
- package/src/lib/bulk-filter/index.spec.js +0 -38
- package/src/lib/bulk-find/README.md +0 -18
- package/src/lib/bulk-find/index.examples.js +0 -19
- package/src/lib/bulk-find/index.js +0 -30
- package/src/lib/bulk-find/index.spec.js +0 -41
- package/src/lib/chatgpt/index.js +0 -163
- package/src/lib/combinations/index.js +0 -30
- package/src/lib/combinations/index.spec.js +0 -23
- package/src/lib/editor/index.js +0 -31
- package/src/lib/functional/index.js +0 -28
- package/src/lib/logger-service/index.js +0 -32
- package/src/lib/parse-js-parts/index.js +0 -321
- package/src/lib/parse-js-parts/index.spec.js +0 -156
- package/src/lib/parse-llm-list/README.md +0 -39
- package/src/lib/parse-llm-list/index.js +0 -54
- package/src/lib/parse-llm-list/index.spec.js +0 -59
- package/src/lib/path-aliases/index.js +0 -37
- package/src/lib/path-aliases/index.spec.js +0 -64
- package/src/lib/pave/index.js +0 -34
- package/src/lib/pave/index.spec.js +0 -76
- package/src/lib/prompt-cache/index.js +0 -50
- package/src/lib/retry/index.js +0 -66
- package/src/lib/retry/index.spec.js +0 -86
- package/src/lib/ring-buffer/README.md +0 -460
- package/src/lib/ring-buffer/index.js +0 -1074
- package/src/lib/search-best-first/city-walk.spec.js +0 -37
- package/src/lib/search-best-first/index.js +0 -97
- package/src/lib/search-best-first/index.spec.js +0 -35
- package/src/lib/search-js-files/code-features-property-definitions.json +0 -123
- package/src/lib/search-js-files/index.examples.js +0 -22
- package/src/lib/search-js-files/index.js +0 -155
- package/src/lib/search-js-files/index.spec.js +0 -34
- package/src/lib/search-js-files/scan-file.js +0 -242
- package/src/lib/shorten-text/index.js +0 -25
- package/src/lib/shorten-text/index.spec.js +0 -68
- package/src/lib/strip-numeric/index.js +0 -5
- package/src/lib/strip-response/index.js +0 -30
- package/src/lib/template-replace/index.js +0 -23
- package/src/lib/template-replace/index.spec.js +0 -60
- package/src/lib/timed-abort-controller/index.js +0 -41
- package/src/lib/to-bool/index.js +0 -8
- package/src/lib/to-date/index.js +0 -11
- package/src/lib/to-enum/index.js +0 -14
- package/src/lib/to-number/index.js +0 -12
- package/src/lib/to-number-with-units/index.js +0 -51
- package/src/lib/transcribe/index.js +0 -78
- package/src/prompts/README.md +0 -17
- package/src/prompts/as-enum.js +0 -5
- package/src/prompts/as-json-schema.js +0 -9
- package/src/prompts/as-object-with-schema.js +0 -26
- package/src/prompts/as-schema-org-text.js +0 -25
- package/src/prompts/as-schema-org-type.js +0 -1
- package/src/prompts/blog-post.js +0 -7
- package/src/prompts/code-features.js +0 -24
- package/src/prompts/constants.js +0 -101
- package/src/prompts/features-json-schema.js +0 -27
- package/src/prompts/generate-collection.js +0 -26
- package/src/prompts/generate-list.js +0 -48
- package/src/prompts/generate-questions.js +0 -19
- package/src/prompts/index.js +0 -20
- package/src/prompts/intent.js +0 -60
- package/src/prompts/output-succinct-names.js +0 -3
- package/src/prompts/select-from-threshold.js +0 -17
- package/src/prompts/sort.js +0 -31
- package/src/prompts/style.js +0 -38
- package/src/prompts/summarize.js +0 -13
- package/src/prompts/token-budget.js +0 -3
- package/src/prompts/wrap-list.js +0 -11
- package/src/prompts/wrap-variable.js +0 -36
- package/src/services/llm-model/global-overrides.spec.js +0 -432
- package/src/services/llm-model/index.js +0 -308
- package/src/services/llm-model/model.js +0 -21
- package/src/services/llm-model/negotiate.spec.js +0 -447
- package/src/services/redis/index.js +0 -147
- package/src/test/setup.js +0 -20
- package/src/verblets/README.md +0 -26
- package/src/verblets/auto/index.examples.js +0 -31
- package/src/verblets/auto/index.js +0 -28
- package/src/verblets/auto/index.spec.js +0 -32
- package/src/verblets/bool/README.md +0 -36
- package/src/verblets/bool/index.examples.js +0 -80
- package/src/verblets/bool/index.js +0 -25
- package/src/verblets/bool/index.schema.json +0 -14
- package/src/verblets/bool/index.spec.js +0 -33
- package/src/verblets/central-tendency/README.md +0 -166
- package/src/verblets/central-tendency/central-tendency-result.json +0 -24
- package/src/verblets/central-tendency/index.examples.js +0 -196
- package/src/verblets/central-tendency/index.js +0 -171
- package/src/verblets/central-tendency/index.spec.js +0 -148
- package/src/verblets/enum/index.examples.js +0 -30
- package/src/verblets/enum/index.js +0 -18
- package/src/verblets/enum/index.spec.js +0 -35
- package/src/verblets/expect/README.md +0 -64
- package/src/verblets/expect/index.examples.js +0 -109
- package/src/verblets/expect/index.js +0 -75
- package/src/verblets/expect/index.spec.js +0 -127
- package/src/verblets/intent/index.examples.js +0 -139
- package/src/verblets/intent/index.js +0 -60
- package/src/verblets/intent/index.spec.js +0 -31
- package/src/verblets/intersection/README.md +0 -16
- package/src/verblets/intersection/index.examples.js +0 -89
- package/src/verblets/intersection/index.js +0 -84
- package/src/verblets/intersection/index.spec.js +0 -60
- package/src/verblets/intersection/intersection-result.json +0 -16
- package/src/verblets/list-expand/README.md +0 -10
- package/src/verblets/list-expand/index.examples.js +0 -14
- package/src/verblets/list-expand/index.js +0 -104
- package/src/verblets/list-expand/index.spec.js +0 -18
- package/src/verblets/list-expand/list-expand-result.json +0 -16
- package/src/verblets/list-filter/README.md +0 -22
- package/src/verblets/list-filter/index.examples.js +0 -26
- package/src/verblets/list-filter/index.js +0 -18
- package/src/verblets/list-filter/index.spec.js +0 -19
- package/src/verblets/list-find/README.md +0 -11
- package/src/verblets/list-find/index.examples.js +0 -15
- package/src/verblets/list-find/index.js +0 -17
- package/src/verblets/list-find/index.spec.js +0 -19
- package/src/verblets/list-group/README.md +0 -16
- package/src/verblets/list-group/index.examples.js +0 -16
- package/src/verblets/list-group/index.js +0 -112
- package/src/verblets/list-group/index.spec.js +0 -35
- package/src/verblets/list-group/list-group-result.json +0 -16
- package/src/verblets/list-map/README.md +0 -11
- package/src/verblets/list-map/index.examples.js +0 -15
- package/src/verblets/list-map/index.js +0 -26
- package/src/verblets/list-map/index.spec.js +0 -17
- package/src/verblets/list-reduce/README.md +0 -10
- package/src/verblets/list-reduce/index.examples.js +0 -14
- package/src/verblets/list-reduce/index.js +0 -21
- package/src/verblets/list-reduce/index.spec.js +0 -27
- package/src/verblets/list-reduce/index.spec.jsx +0 -27
- package/src/verblets/name/README.md +0 -15
- package/src/verblets/name/index.examples.js +0 -28
- package/src/verblets/name/index.js +0 -19
- package/src/verblets/name/index.spec.js +0 -33
- package/src/verblets/name-similar-to/README.md +0 -26
- package/src/verblets/name-similar-to/index.examples.js +0 -18
- package/src/verblets/name-similar-to/index.js +0 -20
- package/src/verblets/name-similar-to/index.spec.js +0 -13
- package/src/verblets/number/index.examples.js +0 -199
- package/src/verblets/number/index.js +0 -25
- package/src/verblets/number/index.spec.js +0 -33
- package/src/verblets/number-with-units/index.examples.js +0 -38
- package/src/verblets/number-with-units/index.js +0 -84
- package/src/verblets/number-with-units/index.spec.js +0 -46
- package/src/verblets/number-with-units/number-with-units-result.json +0 -23
- package/src/verblets/schema-org/index.examples.js +0 -51
- package/src/verblets/schema-org/index.js +0 -37
- package/src/verblets/schema-org/index.spec.js +0 -39
- package/src/verblets/sentiment/README.md +0 -10
- package/src/verblets/sentiment/index.examples.js +0 -20
- package/src/verblets/sentiment/index.js +0 -9
- package/src/verblets/sentiment/index.spec.js +0 -20
- package/src/verblets/to-object/README.md +0 -38
- package/src/verblets/to-object/index.examples.js +0 -29
- package/src/verblets/to-object/index.js +0 -131
- package/src/verblets/to-object/index.spec.js +0 -71
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import 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
|
-
}
|
|
17
|
-
|
|
18
|
-
// Handle constraint-based validations (format: "Given this constraint:")
|
|
19
|
-
if (prompt.includes('Given this constraint:')) {
|
|
20
|
-
if (prompt.includes('Is this a greeting?') && prompt.includes('Hello world!')) {
|
|
21
|
-
return 'True';
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
if (prompt.includes('Is this text professional and grammatically correct?')) {
|
|
25
|
-
if (prompt.includes('well-written, professional email')) {
|
|
26
|
-
return 'True';
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (prompt.includes('Does this person data look realistic?')) {
|
|
31
|
-
if (prompt.includes('John Doe') && prompt.includes('"age": 30')) {
|
|
32
|
-
return 'True';
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (prompt.includes('Is this recommendation specific and actionable?')) {
|
|
37
|
-
if (prompt.includes('Increase marketing budget by 20%')) {
|
|
38
|
-
return 'True';
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Default to False for unmatched cases
|
|
44
|
-
return 'False';
|
|
45
|
-
}),
|
|
46
|
-
}));
|
|
47
|
-
|
|
48
|
-
describe('expect verblet', () => {
|
|
49
|
-
it(
|
|
50
|
-
'should pass for exact equality',
|
|
51
|
-
async () => {
|
|
52
|
-
const result = await aiExpect('hello').toEqual('hello', { throws: false });
|
|
53
|
-
expect(result).toBe(true);
|
|
54
|
-
},
|
|
55
|
-
longTestTimeout
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
it(
|
|
59
|
-
'should pass for constraint-based validation',
|
|
60
|
-
async () => {
|
|
61
|
-
const result = await aiExpect('Hello world!').toSatisfy('Is this a greeting?', {
|
|
62
|
-
throws: false,
|
|
63
|
-
});
|
|
64
|
-
expect(result).toBe(true);
|
|
65
|
-
},
|
|
66
|
-
longTestTimeout
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
it(
|
|
70
|
-
'should fail for non-matching values',
|
|
71
|
-
async () => {
|
|
72
|
-
const result = await aiExpect('goodbye').toEqual('hello', {
|
|
73
|
-
throws: false,
|
|
74
|
-
});
|
|
75
|
-
expect(result).toBe(false);
|
|
76
|
-
},
|
|
77
|
-
longTestTimeout
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
it(
|
|
81
|
-
'should validate content quality',
|
|
82
|
-
async () => {
|
|
83
|
-
const result = await aiExpect(
|
|
84
|
-
'This is a well-written, professional email with proper grammar.'
|
|
85
|
-
).toSatisfy('Is this text professional and grammatically correct?', {
|
|
86
|
-
throws: false,
|
|
87
|
-
});
|
|
88
|
-
expect(result).toBe(true);
|
|
89
|
-
},
|
|
90
|
-
longTestTimeout
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
it(
|
|
94
|
-
'should validate data structures',
|
|
95
|
-
async () => {
|
|
96
|
-
const result = await aiExpect({ name: 'John Doe', age: 30, city: 'New York' }).toSatisfy(
|
|
97
|
-
'Does this person data look realistic?',
|
|
98
|
-
{ throws: false }
|
|
99
|
-
);
|
|
100
|
-
expect(result).toBe(true);
|
|
101
|
-
},
|
|
102
|
-
longTestTimeout
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
it(
|
|
106
|
-
'should handle business logic validation',
|
|
107
|
-
async () => {
|
|
108
|
-
const result = await aiExpect(
|
|
109
|
-
'Increase marketing budget by 20% for Q4 to boost holiday sales'
|
|
110
|
-
).toSatisfy('Is this recommendation specific and actionable?', {
|
|
111
|
-
throws: false,
|
|
112
|
-
});
|
|
113
|
-
expect(result).toBe(true);
|
|
114
|
-
},
|
|
115
|
-
longTestTimeout
|
|
116
|
-
);
|
|
117
|
-
|
|
118
|
-
it(
|
|
119
|
-
'should throw by default on failure',
|
|
120
|
-
async () => {
|
|
121
|
-
await expect(async () => {
|
|
122
|
-
await aiExpect('hello').toEqual('goodbye');
|
|
123
|
-
}).rejects.toThrow('LLM assertion failed');
|
|
124
|
-
},
|
|
125
|
-
longTestTimeout
|
|
126
|
-
);
|
|
127
|
-
});
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import Ajv from 'ajv';
|
|
2
|
-
import fs from 'node:fs/promises';
|
|
3
|
-
import { describe, expect, it, beforeAll, afterAll } from 'vitest';
|
|
4
|
-
import { expect as aiExpect } from '../../chains/expect/index.js';
|
|
5
|
-
import { longTestTimeout } from '../../constants/common.js';
|
|
6
|
-
import { fileURLToPath } from 'url';
|
|
7
|
-
import { dirname, join } from 'path';
|
|
8
|
-
|
|
9
|
-
import intent from './index.js';
|
|
10
|
-
|
|
11
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
-
const __dirname = dirname(__filename);
|
|
13
|
-
|
|
14
|
-
async function getIntentSchema() {
|
|
15
|
-
return JSON.parse(await fs.readFile(join(__dirname, '../../json-schemas/intent.json')));
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const examples = [
|
|
19
|
-
{
|
|
20
|
-
inputs: { text: 'Give me a flight to Burgas' },
|
|
21
|
-
want: { resultSchema: getIntentSchema },
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
inputs: {
|
|
25
|
-
text: 'Lookup a song by the quote \
|
|
26
|
-
"I just gotta tell you how I\'m feeling"',
|
|
27
|
-
},
|
|
28
|
-
want: { resultSchema: getIntentSchema },
|
|
29
|
-
},
|
|
30
|
-
];
|
|
31
|
-
|
|
32
|
-
describe('Intent verblet', () => {
|
|
33
|
-
// Set environment mode to 'none' for all tests to avoid throwing
|
|
34
|
-
const originalMode = process.env.LLM_EXPECT_MODE;
|
|
35
|
-
|
|
36
|
-
beforeAll(() => {
|
|
37
|
-
process.env.LLM_EXPECT_MODE = 'none';
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
afterAll(() => {
|
|
41
|
-
if (originalMode !== undefined) {
|
|
42
|
-
process.env.LLM_EXPECT_MODE = originalMode;
|
|
43
|
-
} else {
|
|
44
|
-
delete process.env.LLM_EXPECT_MODE;
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
examples.forEach((example) => {
|
|
49
|
-
it(
|
|
50
|
-
example.inputs.text,
|
|
51
|
-
async () => {
|
|
52
|
-
const result = await intent({ text: example.inputs.text });
|
|
53
|
-
|
|
54
|
-
if (example.want.resultSchema) {
|
|
55
|
-
const schema = await example.want.resultSchema();
|
|
56
|
-
const ajv = new Ajv();
|
|
57
|
-
const validate = ajv.compile(schema);
|
|
58
|
-
|
|
59
|
-
const isValid = validate(result);
|
|
60
|
-
if (!isValid) {
|
|
61
|
-
console.error('Validation errors:');
|
|
62
|
-
console.error(validate.errors);
|
|
63
|
-
console.error('Returned result:');
|
|
64
|
-
console.error(JSON.stringify(result, null, 2));
|
|
65
|
-
}
|
|
66
|
-
expect(isValid).toStrictEqual(true);
|
|
67
|
-
|
|
68
|
-
// LLM assertion to validate intent extraction quality
|
|
69
|
-
const intentMakesSense = await aiExpect(
|
|
70
|
-
`Original text: "${example.inputs.text}" was parsed into an intent object`
|
|
71
|
-
).toSatisfy('Does this seem like a reasonable intent extraction?');
|
|
72
|
-
expect(intentMakesSense).toBe(true);
|
|
73
|
-
|
|
74
|
-
// Additional assertion for intent completeness
|
|
75
|
-
const hasBasicInfo = await aiExpect(JSON.stringify(result)).toSatisfy(
|
|
76
|
-
'Does this intent object contain some useful information?'
|
|
77
|
-
);
|
|
78
|
-
expect(hasBasicInfo).toBe(true);
|
|
79
|
-
}
|
|
80
|
-
},
|
|
81
|
-
longTestTimeout
|
|
82
|
-
);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it(
|
|
86
|
-
'should extract travel booking intent correctly',
|
|
87
|
-
async () => {
|
|
88
|
-
const travelRequest =
|
|
89
|
-
'Book me a round-trip flight from New York to Tokyo for next month, preferably business class';
|
|
90
|
-
const result = await intent({ text: travelRequest });
|
|
91
|
-
|
|
92
|
-
// Traditional schema validation
|
|
93
|
-
const schema = await getIntentSchema();
|
|
94
|
-
const ajv = new Ajv();
|
|
95
|
-
const validate = ajv.compile(schema);
|
|
96
|
-
expect(validate(result)).toBe(true);
|
|
97
|
-
|
|
98
|
-
// LLM assertions for travel-specific validation
|
|
99
|
-
const isTravelRelated = await aiExpect(`Intent extracted from: "${travelRequest}"`).toSatisfy(
|
|
100
|
-
'Is this request related to travel or transportation?'
|
|
101
|
-
);
|
|
102
|
-
expect(isTravelRelated).toBe(true);
|
|
103
|
-
|
|
104
|
-
const hasLocationInfo = await aiExpect(JSON.stringify(result)).toSatisfy(
|
|
105
|
-
'Does this intent mention any locations or destinations?'
|
|
106
|
-
);
|
|
107
|
-
expect(hasLocationInfo).toBe(true);
|
|
108
|
-
},
|
|
109
|
-
longTestTimeout
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
it(
|
|
113
|
-
'should handle entertainment search intent',
|
|
114
|
-
async () => {
|
|
115
|
-
const musicQuery =
|
|
116
|
-
'Find that song that goes "Never gonna give you up, never gonna let you down"';
|
|
117
|
-
const result = await intent({ text: musicQuery });
|
|
118
|
-
|
|
119
|
-
// Schema validation
|
|
120
|
-
const schema = await getIntentSchema();
|
|
121
|
-
const ajv = new Ajv();
|
|
122
|
-
const validate = ajv.compile(schema);
|
|
123
|
-
expect(validate(result)).toBe(true);
|
|
124
|
-
|
|
125
|
-
// LLM assertion for entertainment intent
|
|
126
|
-
const isEntertainmentRelated = await aiExpect(
|
|
127
|
-
`Intent extracted from: "${musicQuery}"`
|
|
128
|
-
).toSatisfy('Is this request related to music or entertainment?');
|
|
129
|
-
expect(isEntertainmentRelated).toBe(true);
|
|
130
|
-
|
|
131
|
-
// Validate that the intent captures the search criteria
|
|
132
|
-
const mentionsLyrics = await aiExpect(JSON.stringify(result)).toSatisfy(
|
|
133
|
-
'Does this intent mention song lyrics or music search?'
|
|
134
|
-
);
|
|
135
|
-
expect(mentionsLyrics).toBe(true);
|
|
136
|
-
},
|
|
137
|
-
longTestTimeout
|
|
138
|
-
);
|
|
139
|
-
});
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { fileURLToPath } from 'node:url';
|
|
4
|
-
import chatGPT from '../../lib/chatgpt/index.js';
|
|
5
|
-
import { constants as promptConstants } from '../../prompts/index.js';
|
|
6
|
-
|
|
7
|
-
const { contentIsQuestion } = promptConstants;
|
|
8
|
-
|
|
9
|
-
// Get the directory of this module
|
|
10
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
-
const __dirname = path.dirname(__filename);
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Load the JSON schema for intent results
|
|
15
|
-
* @returns {Promise<Object>} JSON schema for validation
|
|
16
|
-
*/
|
|
17
|
-
async function getIntentSchema() {
|
|
18
|
-
const schemaPath = path.resolve(__dirname, '../../json-schemas/intent.json');
|
|
19
|
-
return JSON.parse(await fs.readFile(schemaPath, 'utf8'));
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Create model options for structured outputs
|
|
24
|
-
* @param {string|Object} llm - LLM model name or configuration object
|
|
25
|
-
* @returns {Promise<Object>} Model options for chatGPT
|
|
26
|
-
*/
|
|
27
|
-
async function createModelOptions(llm = 'fastGoodCheap') {
|
|
28
|
-
const schema = await getIntentSchema();
|
|
29
|
-
|
|
30
|
-
const responseFormat = {
|
|
31
|
-
type: 'json_schema',
|
|
32
|
-
json_schema: {
|
|
33
|
-
name: 'intent_result',
|
|
34
|
-
schema,
|
|
35
|
-
},
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
if (typeof llm === 'string') {
|
|
39
|
-
return {
|
|
40
|
-
modelName: llm,
|
|
41
|
-
response_format: responseFormat,
|
|
42
|
-
};
|
|
43
|
-
} else {
|
|
44
|
-
return {
|
|
45
|
-
...llm,
|
|
46
|
-
response_format: responseFormat,
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export default async function intent({ text, config = {} } = {}) {
|
|
52
|
-
const { llm, ...options } = config;
|
|
53
|
-
const prompt = `${contentIsQuestion} What is the intent of this text?\n\n${text}`;
|
|
54
|
-
|
|
55
|
-
const modelOptions = await createModelOptions(llm);
|
|
56
|
-
const response = await chatGPT(prompt, { modelOptions, ...options });
|
|
57
|
-
|
|
58
|
-
// With structured outputs, response should already be parsed
|
|
59
|
-
return typeof response === 'string' ? JSON.parse(response) : response;
|
|
60
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
-
|
|
3
|
-
import intent from './index.js';
|
|
4
|
-
|
|
5
|
-
vi.mock('../../lib/chatgpt/index.js', () => ({
|
|
6
|
-
default: vi.fn().mockImplementation((text) => {
|
|
7
|
-
if (/a flight to/.test(text)) {
|
|
8
|
-
return '{}';
|
|
9
|
-
}
|
|
10
|
-
return 'undefined';
|
|
11
|
-
}),
|
|
12
|
-
}));
|
|
13
|
-
|
|
14
|
-
const examples = [
|
|
15
|
-
{
|
|
16
|
-
name: 'Basic usage',
|
|
17
|
-
inputs: { text: 'Give me a flight to Burgas' },
|
|
18
|
-
want: { typeOfResult: 'object' },
|
|
19
|
-
},
|
|
20
|
-
];
|
|
21
|
-
|
|
22
|
-
describe('Intent verblet', () => {
|
|
23
|
-
examples.forEach((example) => {
|
|
24
|
-
it(example.name, async () => {
|
|
25
|
-
const result = await intent({ text: example.inputs.text });
|
|
26
|
-
if (example.want.typeOfResult) {
|
|
27
|
-
expect(typeof result).toStrictEqual(example.want.typeOfResult);
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
});
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# intersection
|
|
2
|
-
|
|
3
|
-
Find common threads between multiple items using an LLM. The verblet checks every combination from pairs up to the full set. If no relationship is obvious, an empty array is returned.
|
|
4
|
-
|
|
5
|
-
```javascript
|
|
6
|
-
import intersection from './index.js';
|
|
7
|
-
|
|
8
|
-
await intersection(['smartphone', 'tablet', 'laptop']);
|
|
9
|
-
// => ['Portable electronics', 'Portable computers']
|
|
10
|
-
|
|
11
|
-
// Provide custom instructions for how to find intersections
|
|
12
|
-
await intersection(['car', 'bicycle', 'train'], {
|
|
13
|
-
instructions: 'focus on transportation methods available in a city',
|
|
14
|
-
});
|
|
15
|
-
// => ['Wheeled vehicles', 'Public transit']
|
|
16
|
-
```
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import intersection from './index.js';
|
|
3
|
-
import { longTestTimeout } from '../../constants/common.js';
|
|
4
|
-
import aiExpect from '../expect/index.js';
|
|
5
|
-
|
|
6
|
-
describe('intersection examples', () => {
|
|
7
|
-
it(
|
|
8
|
-
'finds commonalities among devices',
|
|
9
|
-
async () => {
|
|
10
|
-
const result = await intersection(['smartphone', 'laptop', 'tablet']);
|
|
11
|
-
expect(Array.isArray(result), `Saw: ${JSON.stringify(result)}`).toBe(true);
|
|
12
|
-
|
|
13
|
-
// LLM assertion to verify the intersection contains meaningful commonalities
|
|
14
|
-
await aiExpect(result).toSatisfy(
|
|
15
|
-
'should be an array of strings that could reasonably represent commonalities between technology devices',
|
|
16
|
-
{
|
|
17
|
-
context: 'Testing intersection verblet with electronic devices',
|
|
18
|
-
}
|
|
19
|
-
);
|
|
20
|
-
},
|
|
21
|
-
longTestTimeout
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
it(
|
|
25
|
-
'finds commonalities among animals',
|
|
26
|
-
async () => {
|
|
27
|
-
const result = await intersection(['dog', 'cat', 'bird']);
|
|
28
|
-
expect(Array.isArray(result), `Saw: ${JSON.stringify(result)}`).toBe(true);
|
|
29
|
-
|
|
30
|
-
// LLM assertion for animal traits - be more lenient
|
|
31
|
-
await aiExpect(result).toSatisfy(
|
|
32
|
-
'should be an array that represents some form of analysis or commonalities related to animals',
|
|
33
|
-
{
|
|
34
|
-
context: 'Testing intersection verblet with animals',
|
|
35
|
-
}
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
// Just check that it's an array - don't require specific content
|
|
39
|
-
expect(Array.isArray(result)).toBe(true);
|
|
40
|
-
},
|
|
41
|
-
longTestTimeout
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
it(
|
|
45
|
-
'handles abstract concepts',
|
|
46
|
-
async () => {
|
|
47
|
-
const result = await intersection(['love', 'friendship', 'trust']);
|
|
48
|
-
expect(Array.isArray(result), `Saw: ${JSON.stringify(result)}`).toBe(true);
|
|
49
|
-
|
|
50
|
-
// LLM assertion for abstract concept intersections - be more specific
|
|
51
|
-
await aiExpect(result).toSatisfy(
|
|
52
|
-
'should be an array of strings representing common emotional or relational concepts that love, friendship, and trust share (like emotional connection, mutual respect, care, etc.)',
|
|
53
|
-
{
|
|
54
|
-
context: 'Testing intersection verblet with abstract concepts: love, friendship, trust',
|
|
55
|
-
}
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
// Verify it's a non-empty array with string elements
|
|
59
|
-
expect(Array.isArray(result)).toBe(true);
|
|
60
|
-
expect(result.length).toBeGreaterThan(0);
|
|
61
|
-
expect(result.every((item) => typeof item === 'string')).toBe(true);
|
|
62
|
-
},
|
|
63
|
-
longTestTimeout
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
it(
|
|
67
|
-
'works with single item',
|
|
68
|
-
async () => {
|
|
69
|
-
const result = await intersection(['bicycle']);
|
|
70
|
-
expect(Array.isArray(result), `Saw: ${JSON.stringify(result)}`).toBe(true);
|
|
71
|
-
|
|
72
|
-
// Single items should return empty array based on the implementation
|
|
73
|
-
expect(result.length).toBe(0);
|
|
74
|
-
},
|
|
75
|
-
longTestTimeout
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
it(
|
|
79
|
-
'handles empty input gracefully',
|
|
80
|
-
async () => {
|
|
81
|
-
const result = await intersection([]);
|
|
82
|
-
expect(Array.isArray(result), `Saw: ${JSON.stringify(result)}`).toBe(true);
|
|
83
|
-
|
|
84
|
-
// Empty input should return empty array
|
|
85
|
-
expect(result.length).toBe(0);
|
|
86
|
-
},
|
|
87
|
-
longTestTimeout
|
|
88
|
-
);
|
|
89
|
-
});
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { fileURLToPath } from 'node:url';
|
|
4
|
-
import chatGPT from '../../lib/chatgpt/index.js';
|
|
5
|
-
import wrapVariable from '../../prompts/wrap-variable.js';
|
|
6
|
-
import { constants as promptConstants } from '../../prompts/index.js';
|
|
7
|
-
|
|
8
|
-
const { contentIsQuestion, tryCompleteData, onlyJSONStringArray } = promptConstants;
|
|
9
|
-
|
|
10
|
-
// Get the directory of this module
|
|
11
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
-
const __dirname = path.dirname(__filename);
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Load the JSON schema for intersection results
|
|
16
|
-
* @returns {Promise<Object>} JSON schema for validation
|
|
17
|
-
*/
|
|
18
|
-
async function getIntersectionSchema() {
|
|
19
|
-
const schemaPath = path.join(__dirname, 'intersection-result.json');
|
|
20
|
-
return JSON.parse(await fs.readFile(schemaPath, 'utf8'));
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Create model options for structured outputs
|
|
25
|
-
* @param {string|Object} llm - LLM model name or configuration object
|
|
26
|
-
* @returns {Promise<Object>} Model options for chatGPT
|
|
27
|
-
*/
|
|
28
|
-
async function createModelOptions(llm = 'fastGoodCheap') {
|
|
29
|
-
const schema = await getIntersectionSchema();
|
|
30
|
-
|
|
31
|
-
const responseFormat = {
|
|
32
|
-
type: 'json_schema',
|
|
33
|
-
json_schema: {
|
|
34
|
-
name: 'intersection_result',
|
|
35
|
-
schema,
|
|
36
|
-
},
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
if (typeof llm === 'string') {
|
|
40
|
-
return {
|
|
41
|
-
modelName: llm,
|
|
42
|
-
response_format: responseFormat,
|
|
43
|
-
};
|
|
44
|
-
} else {
|
|
45
|
-
return {
|
|
46
|
-
...llm,
|
|
47
|
-
response_format: responseFormat,
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export const buildPrompt = (items, { instructions } = {}) => {
|
|
53
|
-
const itemsList = items.join(' | ');
|
|
54
|
-
const itemsBlock = wrapVariable(itemsList, { tag: 'items' });
|
|
55
|
-
const intro =
|
|
56
|
-
instructions ||
|
|
57
|
-
'List the common features, instances, or relational links that all items share.';
|
|
58
|
-
|
|
59
|
-
return `${contentIsQuestion} ${intro}
|
|
60
|
-
|
|
61
|
-
${itemsBlock}
|
|
62
|
-
|
|
63
|
-
The array should specify items without context, groupings, or any other data--just names.
|
|
64
|
-
|
|
65
|
-
${tryCompleteData} ${onlyJSONStringArray}`;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
export default async function intersection(items, config = {}) {
|
|
69
|
-
if (!Array.isArray(items) || items.length < 2) return [];
|
|
70
|
-
|
|
71
|
-
const { llm, ...options } = config;
|
|
72
|
-
const modelOptions = await createModelOptions(llm);
|
|
73
|
-
|
|
74
|
-
const output = await chatGPT(buildPrompt(items, options), {
|
|
75
|
-
modelOptions,
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
// With structured outputs, response should already be parsed and validated
|
|
79
|
-
const parsed = typeof output === 'string' ? JSON.parse(output) : output;
|
|
80
|
-
|
|
81
|
-
// Extract the items array from the object structure
|
|
82
|
-
const resultArray = parsed?.items || parsed;
|
|
83
|
-
return Array.isArray(resultArray) ? resultArray.filter(Boolean) : [];
|
|
84
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
-
import intersection from './index.js';
|
|
3
|
-
|
|
4
|
-
vi.mock('../../lib/chatgpt/index.js', () => ({
|
|
5
|
-
default: vi.fn(async (prompt) => {
|
|
6
|
-
console.log('Mock received prompt:', prompt);
|
|
7
|
-
// Look for quoted items in the prompt
|
|
8
|
-
const match = prompt.match(/"([^"]+)"/);
|
|
9
|
-
if (match) {
|
|
10
|
-
const itemsLine = match[1];
|
|
11
|
-
const items = itemsLine.split(' | ');
|
|
12
|
-
const commonalities = items.map((item) => `common: ${item}`);
|
|
13
|
-
// Add combinations
|
|
14
|
-
for (let i = 0; i < items.length; i++) {
|
|
15
|
-
for (let j = i + 1; j < items.length; j++) {
|
|
16
|
-
commonalities.push(`common: ${items[i]} | ${items[j]}`);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
// Add all items combination
|
|
20
|
-
if (items.length > 2) {
|
|
21
|
-
commonalities.push(`common: ${items.join(' | ')}`);
|
|
22
|
-
}
|
|
23
|
-
console.log('Mock returning:', JSON.stringify(commonalities));
|
|
24
|
-
return JSON.stringify(commonalities);
|
|
25
|
-
}
|
|
26
|
-
console.log('Mock: no match found, returning empty array');
|
|
27
|
-
return JSON.stringify([]);
|
|
28
|
-
}),
|
|
29
|
-
}));
|
|
30
|
-
|
|
31
|
-
describe('intersection verblet', () => {
|
|
32
|
-
it('describes commonalities between sets', async () => {
|
|
33
|
-
const result = await intersection(['a', 'b', 'c']);
|
|
34
|
-
expect(result).toStrictEqual([
|
|
35
|
-
'common: a',
|
|
36
|
-
'common: b',
|
|
37
|
-
'common: c',
|
|
38
|
-
'common: a | b',
|
|
39
|
-
'common: a | c',
|
|
40
|
-
'common: b | c',
|
|
41
|
-
'common: a | b | c',
|
|
42
|
-
]);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('includes custom instructions in the prompt', async () => {
|
|
46
|
-
const chatGPT = (await import('../../lib/chatgpt/index.js')).default;
|
|
47
|
-
await intersection(['x', 'y', 'z'], { instructions: 'focus on features' });
|
|
48
|
-
expect(chatGPT).toHaveBeenCalledWith(
|
|
49
|
-
expect.stringContaining('focus on features'),
|
|
50
|
-
expect.any(Object)
|
|
51
|
-
);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('returns empty array when model returns empty response', async () => {
|
|
55
|
-
const chatGPT = (await import('../../lib/chatgpt/index.js')).default;
|
|
56
|
-
chatGPT.mockResolvedValueOnce('[]');
|
|
57
|
-
const result = await intersection(['x', 'y']);
|
|
58
|
-
expect(result).toStrictEqual([]);
|
|
59
|
-
});
|
|
60
|
-
});
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
-
"type": "object",
|
|
4
|
-
"properties": {
|
|
5
|
-
"items": {
|
|
6
|
-
"type": "array",
|
|
7
|
-
"description": "Array of common features, instances, or relational links shared by all items",
|
|
8
|
-
"items": {
|
|
9
|
-
"type": "string",
|
|
10
|
-
"description": "A common feature, instance, or relational link"
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
},
|
|
14
|
-
"required": ["items"],
|
|
15
|
-
"additionalProperties": false
|
|
16
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
# list-expand
|
|
2
|
-
|
|
3
|
-
Generate additional items that fit naturally with the given list. The function sends the provided items to ChatGPT and requests more entries of the same kind, returning the expanded list.
|
|
4
|
-
|
|
5
|
-
```javascript
|
|
6
|
-
import listExpand from './index.js';
|
|
7
|
-
|
|
8
|
-
await listExpand(['red', 'green'], 5);
|
|
9
|
-
// => ['red', 'green', 'blue', 'yellow', 'purple']
|
|
10
|
-
```
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import listExpand from './index.js';
|
|
3
|
-
import { longTestTimeout } from '../../constants/common.js';
|
|
4
|
-
|
|
5
|
-
describe('list-expand examples', () => {
|
|
6
|
-
it(
|
|
7
|
-
'expands a short list of fruits',
|
|
8
|
-
async () => {
|
|
9
|
-
const result = await listExpand(['apple', 'banana'], 5);
|
|
10
|
-
expect(result.length).toBeGreaterThanOrEqual(5);
|
|
11
|
-
},
|
|
12
|
-
longTestTimeout
|
|
13
|
-
);
|
|
14
|
-
});
|