@far-world-labs/verblets 0.2.0 → 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 -11
- 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 -12
- 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/clear-redis.js +0 -74
- 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/conversation/README.md +0 -26
- package/src/chains/conversation/index.examples.js +0 -398
- package/src/chains/conversation/index.js +0 -126
- package/src/chains/conversation/index.spec.js +0 -148
- package/src/chains/conversation/turn-policies.js +0 -93
- package/src/chains/conversation/turn-policies.md +0 -123
- package/src/chains/conversation/turn-policies.spec.js +0 -135
- 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 -207
- 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 -166
- package/src/chains/intersections/index.examples.js +0 -280
- package/src/chains/intersections/index.js +0 -218
- 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 -366
- package/src/chains/llm-logger/index.js +0 -591
- package/src/chains/llm-logger/index.spec.js +0 -391
- package/src/chains/llm-logger/schema.json +0 -105
- 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 -64
- package/src/chains/set-interval/index.js +0 -152
- 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 -13
- package/src/constants/messages.js +0 -3
- package/src/constants/models.js +0 -184
- package/src/index.js +0 -203
- 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/assert/README.md +0 -84
- package/src/lib/assert/index.js +0 -50
- 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 -82
- package/src/lib/ring-buffer/index.js +0 -235
- package/src/lib/ring-buffer/index.spec.js +0 -388
- 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/conversation-turn/README.md +0 -33
- package/src/verblets/conversation-turn/index.examples.js +0 -218
- package/src/verblets/conversation-turn/index.js +0 -68
- package/src/verblets/conversation-turn/index.spec.js +0 -77
- package/src/verblets/conversation-turn-multi/README.md +0 -31
- package/src/verblets/conversation-turn-multi/index.examples.js +0 -160
- package/src/verblets/conversation-turn-multi/index.js +0 -104
- package/src/verblets/conversation-turn-multi/index.spec.js +0 -63
- 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 -125
- 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/people-list/README.md +0 -28
- package/src/verblets/people-list/index.examples.js +0 -184
- package/src/verblets/people-list/index.js +0 -44
- package/src/verblets/people-list/index.spec.js +0 -49
- 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,280 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import intersections from './index.js';
|
|
3
|
-
import { expect as aiExpect } from '../expect/index.js';
|
|
4
|
-
import { longTestTimeout, shouldRunLongExamples } from '../../constants/common.js';
|
|
5
|
-
|
|
6
|
-
describe('intersections chain examples', () => {
|
|
7
|
-
it.skipIf(!shouldRunLongExamples)(
|
|
8
|
-
'analyzes technology categories comprehensively',
|
|
9
|
-
async () => {
|
|
10
|
-
const result = await intersections(['software', 'hardware', 'networking']);
|
|
11
|
-
|
|
12
|
-
// Basic validation
|
|
13
|
-
expect(typeof result).toBe('object');
|
|
14
|
-
expect(result).not.toBeNull();
|
|
15
|
-
|
|
16
|
-
// Should have meaningful intersections
|
|
17
|
-
expect(Object.keys(result).length).toBeGreaterThan(0);
|
|
18
|
-
|
|
19
|
-
// Validate structure of each intersection
|
|
20
|
-
// eslint-disable-next-line no-unused-vars
|
|
21
|
-
for (const [_comboKey, intersection] of Object.entries(result)) {
|
|
22
|
-
expect(intersection.combination).toBeDefined();
|
|
23
|
-
expect(intersection.description).toBeDefined();
|
|
24
|
-
expect(intersection.elements).toBeDefined();
|
|
25
|
-
expect(Array.isArray(intersection.combination)).toBe(true);
|
|
26
|
-
expect(Array.isArray(intersection.elements)).toBe(true);
|
|
27
|
-
expect(typeof intersection.description).toBe('string');
|
|
28
|
-
expect(intersection.combination.length).toBeGreaterThanOrEqual(2);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// AI validation of technology intersections
|
|
32
|
-
const [hasValidTechIntersections] = await aiExpect(
|
|
33
|
-
result,
|
|
34
|
-
undefined,
|
|
35
|
-
'Should contain meaningful intersections between technology categories with relevant examples'
|
|
36
|
-
);
|
|
37
|
-
expect(hasValidTechIntersections).toBe(true);
|
|
38
|
-
},
|
|
39
|
-
longTestTimeout
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
it.skipIf(!shouldRunLongExamples)(
|
|
43
|
-
'handles diverse categories with meaningful intersections',
|
|
44
|
-
async () => {
|
|
45
|
-
const result = await intersections(['art', 'science']);
|
|
46
|
-
|
|
47
|
-
// Basic validation
|
|
48
|
-
expect(typeof result).toBe('object');
|
|
49
|
-
expect(result).not.toBeNull();
|
|
50
|
-
|
|
51
|
-
// Should have at least one intersection for these broad categories
|
|
52
|
-
if (Object.keys(result).length > 0) {
|
|
53
|
-
const firstIntersection = Object.values(result)[0];
|
|
54
|
-
expect(Array.isArray(firstIntersection.elements)).toBe(true);
|
|
55
|
-
expect(typeof firstIntersection.description).toBe('string');
|
|
56
|
-
expect(Array.isArray(firstIntersection.combination)).toBe(true);
|
|
57
|
-
expect(firstIntersection.combination).toContain('art');
|
|
58
|
-
expect(firstIntersection.combination).toContain('science');
|
|
59
|
-
|
|
60
|
-
// AI validation of meaningful intersections
|
|
61
|
-
const [hasMeaningfulIntersections] = await aiExpect(
|
|
62
|
-
result,
|
|
63
|
-
undefined,
|
|
64
|
-
'Should contain meaningful intersections between art and science with relevant examples'
|
|
65
|
-
);
|
|
66
|
-
expect(hasMeaningfulIntersections).toBe(true);
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
longTestTimeout
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
it.skipIf(!shouldRunLongExamples)(
|
|
73
|
-
'validates schema compliance and structure quality',
|
|
74
|
-
async () => {
|
|
75
|
-
const result = await intersections(['music', 'mathematics']);
|
|
76
|
-
|
|
77
|
-
// Schema validation - should be an object
|
|
78
|
-
expect(typeof result).toBe('object');
|
|
79
|
-
expect(result).not.toBeNull();
|
|
80
|
-
expect(Array.isArray(result)).toBe(false);
|
|
81
|
-
|
|
82
|
-
// If we have results, validate strict schema compliance
|
|
83
|
-
if (Object.keys(result).length > 0) {
|
|
84
|
-
for (const [comboKey, intersection] of Object.entries(result)) {
|
|
85
|
-
// Key format validation (should be "category + category")
|
|
86
|
-
expect(comboKey).toMatch(/^.+ \+ .+$/);
|
|
87
|
-
|
|
88
|
-
// Required properties
|
|
89
|
-
expect(intersection).toHaveProperty('combination');
|
|
90
|
-
expect(intersection).toHaveProperty('description');
|
|
91
|
-
expect(intersection).toHaveProperty('elements');
|
|
92
|
-
|
|
93
|
-
// Type validation
|
|
94
|
-
expect(Array.isArray(intersection.combination)).toBe(true);
|
|
95
|
-
expect(typeof intersection.description).toBe('string');
|
|
96
|
-
expect(Array.isArray(intersection.elements)).toBe(true);
|
|
97
|
-
|
|
98
|
-
// Content validation
|
|
99
|
-
expect(intersection.combination.length).toBeGreaterThanOrEqual(2);
|
|
100
|
-
expect(intersection.description.length).toBeGreaterThan(0);
|
|
101
|
-
expect(intersection.combination).toContain('music');
|
|
102
|
-
expect(intersection.combination).toContain('mathematics');
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// AI validation of music-math intersections
|
|
106
|
-
const [hasValidMusicMathIntersections] = await aiExpect(
|
|
107
|
-
Object.values(result)[0].elements,
|
|
108
|
-
undefined,
|
|
109
|
-
'Should contain examples that genuinely belong to both music and mathematics'
|
|
110
|
-
);
|
|
111
|
-
expect(hasValidMusicMathIntersections).toBe(true);
|
|
112
|
-
}
|
|
113
|
-
},
|
|
114
|
-
longTestTimeout
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
it.skipIf(!shouldRunLongExamples)(
|
|
118
|
-
'handles complex multi-category intersections',
|
|
119
|
-
async () => {
|
|
120
|
-
const result = await intersections(['biology', 'chemistry', 'physics'], {
|
|
121
|
-
maxSize: 3,
|
|
122
|
-
minSize: 2,
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
// Basic validation
|
|
126
|
-
expect(typeof result).toBe('object');
|
|
127
|
-
expect(result).not.toBeNull();
|
|
128
|
-
|
|
129
|
-
// Should handle multiple intersection types
|
|
130
|
-
if (Object.keys(result).length > 0) {
|
|
131
|
-
let hasTwoWayIntersection = false;
|
|
132
|
-
let _hasThreeWayIntersection = false;
|
|
133
|
-
|
|
134
|
-
// eslint-disable-next-line no-unused-vars
|
|
135
|
-
for (const [_comboKey, intersection] of Object.entries(result)) {
|
|
136
|
-
const comboSize = intersection.combination.length;
|
|
137
|
-
|
|
138
|
-
if (comboSize === 2) hasTwoWayIntersection = true;
|
|
139
|
-
// eslint-disable-next-line no-unused-vars
|
|
140
|
-
if (comboSize === 3) _hasThreeWayIntersection = true;
|
|
141
|
-
|
|
142
|
-
// Validate structure
|
|
143
|
-
expect(Array.isArray(intersection.combination)).toBe(true);
|
|
144
|
-
expect(typeof intersection.description).toBe('string');
|
|
145
|
-
expect(Array.isArray(intersection.elements)).toBe(true);
|
|
146
|
-
|
|
147
|
-
// All combinations should be from our input categories
|
|
148
|
-
for (const category of intersection.combination) {
|
|
149
|
-
expect(['biology', 'chemistry', 'physics']).toContain(category);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Should have at least two-way intersections for science categories
|
|
154
|
-
expect(hasTwoWayIntersection).toBe(true);
|
|
155
|
-
|
|
156
|
-
// AI validation of scientific intersections
|
|
157
|
-
const [hasValidScienceIntersections] = await aiExpect(
|
|
158
|
-
result,
|
|
159
|
-
undefined,
|
|
160
|
-
'Should contain meaningful intersections between scientific disciplines with relevant examples'
|
|
161
|
-
);
|
|
162
|
-
expect(hasValidScienceIntersections).toBe(true);
|
|
163
|
-
}
|
|
164
|
-
},
|
|
165
|
-
longTestTimeout
|
|
166
|
-
);
|
|
167
|
-
|
|
168
|
-
it(
|
|
169
|
-
'handles single category appropriately',
|
|
170
|
-
async () => {
|
|
171
|
-
const result = await intersections(['photography']);
|
|
172
|
-
|
|
173
|
-
// Should return empty object since intersections require multiple items
|
|
174
|
-
expect(Object.keys(result).length).toBe(0);
|
|
175
|
-
|
|
176
|
-
// Validate single category handling
|
|
177
|
-
const [isEmptyObject] = await aiExpect(
|
|
178
|
-
result,
|
|
179
|
-
undefined,
|
|
180
|
-
'Should be an empty object since intersections require multiple categories'
|
|
181
|
-
);
|
|
182
|
-
expect(isEmptyObject).toBe(true);
|
|
183
|
-
},
|
|
184
|
-
longTestTimeout
|
|
185
|
-
);
|
|
186
|
-
|
|
187
|
-
it.skipIf(!shouldRunLongExamples)(
|
|
188
|
-
'produces consistent and logical results with quality validation',
|
|
189
|
-
async () => {
|
|
190
|
-
const result = await intersections(['literature', 'psychology'], {
|
|
191
|
-
goodnessScore: 8, // Higher quality threshold
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
// Basic validation - should be an object
|
|
195
|
-
expect(typeof result).toBe('object');
|
|
196
|
-
expect(result).not.toBeNull();
|
|
197
|
-
|
|
198
|
-
// If we have results, validate high-quality structure
|
|
199
|
-
if (Object.keys(result).length > 0) {
|
|
200
|
-
// eslint-disable-next-line no-unused-vars
|
|
201
|
-
for (const [_comboKey, intersection] of Object.entries(result)) {
|
|
202
|
-
// Basic structure checks
|
|
203
|
-
expect(intersection.combination).toBeDefined();
|
|
204
|
-
expect(intersection.description).toBeDefined();
|
|
205
|
-
expect(intersection.elements).toBeDefined();
|
|
206
|
-
expect(Array.isArray(intersection.combination)).toBe(true);
|
|
207
|
-
expect(Array.isArray(intersection.elements)).toBe(true);
|
|
208
|
-
expect(typeof intersection.description).toBe('string');
|
|
209
|
-
|
|
210
|
-
// Quality checks - should have meaningful content
|
|
211
|
-
expect(intersection.description.length).toBeGreaterThan(10);
|
|
212
|
-
expect(intersection.elements.length).toBeGreaterThan(0);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// AI validation of literature-psychology intersections
|
|
216
|
-
const [hasQualityIntersections] = await aiExpect(
|
|
217
|
-
result,
|
|
218
|
-
undefined,
|
|
219
|
-
'Should contain high-quality intersections between literature and psychology with meaningful examples'
|
|
220
|
-
);
|
|
221
|
-
expect(hasQualityIntersections).toBe(true);
|
|
222
|
-
}
|
|
223
|
-
},
|
|
224
|
-
longTestTimeout
|
|
225
|
-
);
|
|
226
|
-
|
|
227
|
-
it(
|
|
228
|
-
'handles edge cases gracefully',
|
|
229
|
-
async () => {
|
|
230
|
-
const result = await intersections([]);
|
|
231
|
-
|
|
232
|
-
// Validate empty input handling
|
|
233
|
-
const [isEmptyForEmptyInput] = await aiExpect(
|
|
234
|
-
result,
|
|
235
|
-
undefined,
|
|
236
|
-
'Should be an empty object for empty input'
|
|
237
|
-
);
|
|
238
|
-
expect(isEmptyForEmptyInput).toBe(true);
|
|
239
|
-
|
|
240
|
-
// Validate empty result structure
|
|
241
|
-
expect(Object.keys(result).length).toBe(0);
|
|
242
|
-
},
|
|
243
|
-
longTestTimeout
|
|
244
|
-
);
|
|
245
|
-
|
|
246
|
-
it(
|
|
247
|
-
'validates custom instructions and configuration',
|
|
248
|
-
async () => {
|
|
249
|
-
const customInstructions =
|
|
250
|
-
'List concrete project ideas that combine these fields. Avoid abstract themes.';
|
|
251
|
-
const result = await intersections(['engineering', 'design'], {
|
|
252
|
-
instructions: customInstructions,
|
|
253
|
-
batchSize: 2,
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
// Basic validation
|
|
257
|
-
expect(typeof result).toBe('object');
|
|
258
|
-
expect(result).not.toBeNull();
|
|
259
|
-
|
|
260
|
-
// If we have results, validate they follow custom instructions
|
|
261
|
-
if (Object.keys(result).length > 0) {
|
|
262
|
-
const firstIntersection = Object.values(result)[0];
|
|
263
|
-
|
|
264
|
-
// Structure validation
|
|
265
|
-
expect(Array.isArray(firstIntersection.combination)).toBe(true);
|
|
266
|
-
expect(typeof firstIntersection.description).toBe('string');
|
|
267
|
-
expect(Array.isArray(firstIntersection.elements)).toBe(true);
|
|
268
|
-
|
|
269
|
-
// AI validation that results follow custom instructions
|
|
270
|
-
const [followsInstructions] = await aiExpect(
|
|
271
|
-
firstIntersection,
|
|
272
|
-
undefined,
|
|
273
|
-
'Should list specific project ideas and avoid abstract themes as requested in custom instructions'
|
|
274
|
-
);
|
|
275
|
-
expect(followsInstructions).toBe(true);
|
|
276
|
-
}
|
|
277
|
-
},
|
|
278
|
-
longTestTimeout
|
|
279
|
-
);
|
|
280
|
-
});
|
|
@@ -1,218 +0,0 @@
|
|
|
1
|
-
import intersection from '../../verblets/intersection/index.js';
|
|
2
|
-
import { rangeCombinations } from '../../lib/combinations/index.js';
|
|
3
|
-
import chatGPT from '../../lib/chatgpt/index.js';
|
|
4
|
-
import wrapVariable from '../../prompts/wrap-variable.js';
|
|
5
|
-
import { constants as promptConstants } from '../../prompts/index.js';
|
|
6
|
-
import fs from 'node:fs/promises';
|
|
7
|
-
import path from 'node:path';
|
|
8
|
-
import { fileURLToPath } from 'node:url';
|
|
9
|
-
|
|
10
|
-
// Get the directory of this module
|
|
11
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
-
const __dirname = path.dirname(__filename);
|
|
13
|
-
|
|
14
|
-
const { onlyJSONStringArray, strictFormat, contentIsQuestion } = promptConstants;
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Load the JSON schema for intersection results
|
|
18
|
-
* @returns {Promise<Object>} JSON schema for validation
|
|
19
|
-
*/
|
|
20
|
-
async function getIntersectionSchema() {
|
|
21
|
-
const schemaPath = path.join(__dirname, 'intersection-result.json');
|
|
22
|
-
return JSON.parse(await fs.readFile(schemaPath, 'utf8'));
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Generalized prompt for finding intersection elements
|
|
27
|
-
*/
|
|
28
|
-
const INTERSECTION_PROMPT = (categories, instructions) => {
|
|
29
|
-
const basePrompt = `${contentIsQuestion} Find specific examples, instances, or elements that belong to all of these categories: ${categories.join(
|
|
30
|
-
', '
|
|
31
|
-
)}.
|
|
32
|
-
|
|
33
|
-
Focus on items that genuinely exist in the intersection of all categories.`;
|
|
34
|
-
|
|
35
|
-
const instructionsText = instructions ? `\n\nAdditional context: ${instructions}` : '';
|
|
36
|
-
|
|
37
|
-
return `${basePrompt}${instructionsText}
|
|
38
|
-
|
|
39
|
-
${wrapVariable(categories.join(' | '), { tag: 'categories' })}
|
|
40
|
-
|
|
41
|
-
${strictFormat} ${onlyJSONStringArray}`;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Parse elements from LLM response
|
|
46
|
-
*/
|
|
47
|
-
const parseElements = (elementsText) => {
|
|
48
|
-
try {
|
|
49
|
-
const parsed = JSON.parse(elementsText.trim());
|
|
50
|
-
return Array.isArray(parsed) ? parsed.filter(Boolean) : [];
|
|
51
|
-
} catch {
|
|
52
|
-
// Fallback to line-by-line parsing if JSON parsing fails
|
|
53
|
-
return elementsText
|
|
54
|
-
.split('\n')
|
|
55
|
-
.map((line) => line.replace(/^[-*•]\s*/, '').trim())
|
|
56
|
-
.filter(Boolean);
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Process a single combination to get intersection elements and description
|
|
62
|
-
*/
|
|
63
|
-
const processCombo = async (combo, instructions) => {
|
|
64
|
-
const comboKey = combo.join(' + ');
|
|
65
|
-
|
|
66
|
-
// Get elements and description in parallel
|
|
67
|
-
const [elements, intersectionItems] = await Promise.all([
|
|
68
|
-
chatGPT(INTERSECTION_PROMPT(combo, instructions)),
|
|
69
|
-
intersection(combo, { instructions }),
|
|
70
|
-
]);
|
|
71
|
-
|
|
72
|
-
const elementList = parseElements(elements);
|
|
73
|
-
const description = Array.isArray(intersectionItems)
|
|
74
|
-
? intersectionItems.join(', ')
|
|
75
|
-
: String(intersectionItems);
|
|
76
|
-
|
|
77
|
-
return {
|
|
78
|
-
key: comboKey,
|
|
79
|
-
intersection: {
|
|
80
|
-
combination: combo,
|
|
81
|
-
description,
|
|
82
|
-
elements: elementList,
|
|
83
|
-
},
|
|
84
|
-
};
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Find intersections for all combinations of items with consistent results
|
|
89
|
-
*
|
|
90
|
-
* @param {Array} items - Array of items to find intersections between
|
|
91
|
-
* @param {Object} options - Configuration options
|
|
92
|
-
* @param {string} options.instructions - Custom instructions for intersection finding
|
|
93
|
-
* @param {number} options.minSize - Minimum combination size (default: 2)
|
|
94
|
-
* @param {number} options.maxSize - Maximum combination size (default: items.length)
|
|
95
|
-
* @param {number} options.batchSize - Number of combinations to process in parallel (default: 10)
|
|
96
|
-
* @param {string|Object} options.llm - LLM model to use (default: 'fastGoodCheap')
|
|
97
|
-
* @param {boolean} options.useSchemaValidation - Whether to validate results with JSON schema (default: false)
|
|
98
|
-
* @returns {Object} Results with combinations, elements, and intersections
|
|
99
|
-
*/
|
|
100
|
-
export default async function intersections(items, options = {}) {
|
|
101
|
-
if (!Array.isArray(items) || items.length < 2) {
|
|
102
|
-
return {};
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const {
|
|
106
|
-
instructions,
|
|
107
|
-
minSize = 2,
|
|
108
|
-
maxSize = items.length,
|
|
109
|
-
batchSize = 10,
|
|
110
|
-
llm = 'fastGoodCheap',
|
|
111
|
-
useSchemaValidation = false,
|
|
112
|
-
} = options;
|
|
113
|
-
|
|
114
|
-
// Generate all combinations
|
|
115
|
-
const allCombinations = rangeCombinations(items, minSize, maxSize);
|
|
116
|
-
|
|
117
|
-
if (allCombinations.length === 0) {
|
|
118
|
-
return {};
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Process all combinations in batches
|
|
122
|
-
const results = {};
|
|
123
|
-
|
|
124
|
-
for (let i = 0; i < allCombinations.length; i += batchSize) {
|
|
125
|
-
const batch = allCombinations.slice(i, i + batchSize);
|
|
126
|
-
const batchResults = await Promise.all(batch.map((combo) => processCombo(combo, instructions)));
|
|
127
|
-
|
|
128
|
-
// Add batch results to final results
|
|
129
|
-
for (const result of batchResults) {
|
|
130
|
-
results[result.key] = result.intersection;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Validate results with JSON schema if enabled
|
|
135
|
-
if (useSchemaValidation && Object.keys(results).length > 0) {
|
|
136
|
-
const validated = await validateIntersectionResults(results, llm);
|
|
137
|
-
return validated.intersections || results;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
return results;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Create model options with JSON schema validation
|
|
145
|
-
* @param {string|Object} llm - LLM model to use
|
|
146
|
-
* @param {string} schemaName - Name for the JSON schema
|
|
147
|
-
* @returns {Promise<Object>} Model options with schema validation
|
|
148
|
-
*/
|
|
149
|
-
async function createModelOptions(llm = 'fastGoodCheap', schemaName = 'intersection_result') {
|
|
150
|
-
const schema = await getIntersectionSchema();
|
|
151
|
-
|
|
152
|
-
const responseFormat = {
|
|
153
|
-
type: 'json_schema',
|
|
154
|
-
json_schema: {
|
|
155
|
-
name: schemaName,
|
|
156
|
-
schema,
|
|
157
|
-
},
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
if (typeof llm === 'string') {
|
|
161
|
-
return {
|
|
162
|
-
modelName: llm,
|
|
163
|
-
response_format: responseFormat,
|
|
164
|
-
};
|
|
165
|
-
} else {
|
|
166
|
-
return {
|
|
167
|
-
...llm,
|
|
168
|
-
response_format: responseFormat,
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Validate and structure final results using JSON schema
|
|
175
|
-
* @param {Object} intersections - Raw intersection results
|
|
176
|
-
* @param {string|Object} llm - LLM model to use
|
|
177
|
-
* @returns {Promise<Object>} Schema-validated intersection results
|
|
178
|
-
*/
|
|
179
|
-
async function validateIntersectionResults(intersections, llm = 'fastGoodCheap') {
|
|
180
|
-
if (!intersections || Object.keys(intersections).length === 0) {
|
|
181
|
-
return { intersections: {} };
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
const prompt = `Validate and structure these intersection results according to the required schema:
|
|
185
|
-
|
|
186
|
-
${JSON.stringify(intersections, null, 2)}
|
|
187
|
-
|
|
188
|
-
Ensure each intersection has:
|
|
189
|
-
- combination: array of category names
|
|
190
|
-
- description: clear explanation of the intersection
|
|
191
|
-
- elements: array of specific examples that belong to ALL categories
|
|
192
|
-
|
|
193
|
-
Return the properly structured JSON object with an "intersections" property containing the results.`;
|
|
194
|
-
|
|
195
|
-
try {
|
|
196
|
-
const modelOptions = await createModelOptions(llm, 'intersection_result');
|
|
197
|
-
const response = await chatGPT(prompt, { modelOptions });
|
|
198
|
-
const parsed = typeof response === 'string' ? JSON.parse(response) : response;
|
|
199
|
-
|
|
200
|
-
// Extract intersections from the object structure
|
|
201
|
-
const resultIntersections = parsed?.intersections || parsed;
|
|
202
|
-
|
|
203
|
-
// Validate that the result is an object
|
|
204
|
-
if (
|
|
205
|
-
typeof resultIntersections !== 'object' ||
|
|
206
|
-
resultIntersections === null ||
|
|
207
|
-
Array.isArray(resultIntersections)
|
|
208
|
-
) {
|
|
209
|
-
console.warn('Schema validation failed: invalid structure, returning original results');
|
|
210
|
-
return { intersections };
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
return { intersections: resultIntersections };
|
|
214
|
-
} catch (error) {
|
|
215
|
-
console.warn('Schema validation failed, returning original results:', error.message);
|
|
216
|
-
return { intersections };
|
|
217
|
-
}
|
|
218
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
-
"type": "object",
|
|
4
|
-
"description": "Intersection results between categories",
|
|
5
|
-
"properties": {
|
|
6
|
-
"intersections": {
|
|
7
|
-
"type": "object",
|
|
8
|
-
"description": "Map of intersection results keyed by combination identifier",
|
|
9
|
-
"additionalProperties": {
|
|
10
|
-
"type": "object",
|
|
11
|
-
"properties": {
|
|
12
|
-
"combination": {
|
|
13
|
-
"type": "array",
|
|
14
|
-
"items": {
|
|
15
|
-
"type": "string"
|
|
16
|
-
},
|
|
17
|
-
"description": "Array of category names that form this intersection"
|
|
18
|
-
},
|
|
19
|
-
"description": {
|
|
20
|
-
"type": "string",
|
|
21
|
-
"description": "Clear explanation of what this intersection represents"
|
|
22
|
-
},
|
|
23
|
-
"elements": {
|
|
24
|
-
"type": "array",
|
|
25
|
-
"items": {
|
|
26
|
-
"type": "string"
|
|
27
|
-
},
|
|
28
|
-
"description": "Specific examples that belong to ALL categories in the combination"
|
|
29
|
-
}
|
|
30
|
-
},
|
|
31
|
-
"required": ["combination", "description", "elements"],
|
|
32
|
-
"additionalProperties": false
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
},
|
|
36
|
-
"required": ["intersections"],
|
|
37
|
-
"additionalProperties": false
|
|
38
|
-
}
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
|
|
3
|
-
import { longTestTimeout } from '../../constants/common.js';
|
|
4
|
-
import chatGPT from '../../lib/chatgpt/index.js';
|
|
5
|
-
import { asJSONSchema } from '../../prompts/index.js';
|
|
6
|
-
import toObject from '../../verblets/to-object/index.js';
|
|
7
|
-
|
|
8
|
-
import list from './index.js';
|
|
9
|
-
|
|
10
|
-
const examples = [
|
|
11
|
-
{
|
|
12
|
-
inputs: { description: '2021 EV cars' },
|
|
13
|
-
want: { minLength: 10, listContains: 'Model Y' },
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
inputs: {
|
|
17
|
-
description: '2021 EV cars',
|
|
18
|
-
jsonSchemaQuery:
|
|
19
|
-
'make, model, releaseDate (ISO),\
|
|
20
|
-
maxRange (miles), batteryCapacity (kWH), startingCost (USD)',
|
|
21
|
-
},
|
|
22
|
-
want: { minLength: 10, listModelContains: 'Model Y' },
|
|
23
|
-
},
|
|
24
|
-
];
|
|
25
|
-
|
|
26
|
-
describe('List verblet', () => {
|
|
27
|
-
examples.forEach((example) => {
|
|
28
|
-
let jsonSchemaDisplay = '';
|
|
29
|
-
if (example.inputs.jsonSchemaQuery) {
|
|
30
|
-
const jsonSchemaEllipsis = example.inputs.jsonSchemaQuery.length > 10 ? '...' : '';
|
|
31
|
-
jsonSchemaDisplay = ` - ${example.inputs.jsonSchemaQuery.slice(0, 10)}${jsonSchemaEllipsis}`;
|
|
32
|
-
}
|
|
33
|
-
it(
|
|
34
|
-
`${example.inputs.description}${jsonSchemaDisplay}`,
|
|
35
|
-
async () => {
|
|
36
|
-
let schema;
|
|
37
|
-
if (example.inputs.jsonSchemaQuery) {
|
|
38
|
-
schema = await toObject(await chatGPT(asJSONSchema(example.inputs.jsonSchemaQuery)));
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const result = await list(example.inputs.description, {
|
|
42
|
-
schema,
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
if (example.want.minLength) {
|
|
46
|
-
expect(result.length).gt(5);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (example.want.listContains) {
|
|
50
|
-
expect(result.some((item) => item.includes(example.want.listContains))).equals(true);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (example.want.listModelContains) {
|
|
54
|
-
expect(
|
|
55
|
-
result.some((item) => {
|
|
56
|
-
// Handle both string and object results
|
|
57
|
-
if (typeof item === 'string') {
|
|
58
|
-
return item.includes(example.want.listModelContains);
|
|
59
|
-
}
|
|
60
|
-
return item.model?.includes(example.want.listModelContains);
|
|
61
|
-
})
|
|
62
|
-
).equals(true);
|
|
63
|
-
}
|
|
64
|
-
},
|
|
65
|
-
longTestTimeout
|
|
66
|
-
);
|
|
67
|
-
});
|
|
68
|
-
});
|