@far-world-labs/verblets 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +98 -213
- package/dist/index.browser.js +221 -0
- package/dist/index.js +696 -0
- package/dist/shared-CMgpfDG4.js +10714 -0
- package/package.json +38 -15
- 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,86 +0,0 @@
|
|
|
1
|
-
import listMap from '../../verblets/list-map/index.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Map over a list of fragments by calling `listMap` on newline-delimited batches.
|
|
5
|
-
* Missing or mismatched output results in `undefined` entries so callers can
|
|
6
|
-
* selectively retry.
|
|
7
|
-
*
|
|
8
|
-
* @param { string[] } list - array of fragments to process
|
|
9
|
-
* @param { string } instructions - mapping instructions passed to `listMap`
|
|
10
|
-
* @param { object } [config={}] - configuration options
|
|
11
|
-
* @param { number } [config.chunkSize=10] - how many items to send per batch
|
|
12
|
-
* @param { object } [config.llm] - LLM configuration
|
|
13
|
-
* @returns { Promise<(string|undefined)[]> } results aligned with input order
|
|
14
|
-
*/
|
|
15
|
-
const bulkMap = async function (list, instructions, config = {}) {
|
|
16
|
-
const { chunkSize = 10, llm, ...options } = config;
|
|
17
|
-
const results = new Array(list.length);
|
|
18
|
-
const promises = [];
|
|
19
|
-
|
|
20
|
-
for (let i = 0; i < list.length; i += chunkSize) {
|
|
21
|
-
const batch = list.slice(i, i + chunkSize);
|
|
22
|
-
const startIndex = i;
|
|
23
|
-
|
|
24
|
-
const p = Promise.resolve()
|
|
25
|
-
.then(() => listMap(batch, instructions, { llm, ...options }))
|
|
26
|
-
.then((output) => {
|
|
27
|
-
if (output.length !== batch.length) {
|
|
28
|
-
for (let j = 0; j < batch.length; j += 1) {
|
|
29
|
-
results[startIndex + j] = undefined;
|
|
30
|
-
}
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
output.forEach((line, j) => {
|
|
34
|
-
results[startIndex + j] = line;
|
|
35
|
-
});
|
|
36
|
-
})
|
|
37
|
-
.catch(() => {
|
|
38
|
-
for (let j = 0; j < batch.length; j += 1) {
|
|
39
|
-
results[startIndex + j] = undefined;
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
promises.push(p);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
await Promise.all(promises);
|
|
46
|
-
return results;
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Retry only the undefined results from `map` until maxAttempts is reached.
|
|
51
|
-
*
|
|
52
|
-
* @param { string[] } list - array of fragments
|
|
53
|
-
* @param { string } instructions - mapping instructions passed to `listMap`
|
|
54
|
-
* @param { object } [config={}] - configuration options
|
|
55
|
-
* @param { number } [config.chunkSize=10]
|
|
56
|
-
* @param { number } [config.maxAttempts=3]
|
|
57
|
-
* @param { object } [config.llm] - LLM configuration
|
|
58
|
-
* @returns { Promise<(string|undefined)[]> }
|
|
59
|
-
*/
|
|
60
|
-
export const bulkMapRetry = async function (list, instructions, config = {}) {
|
|
61
|
-
const { chunkSize = 10, maxAttempts = 3, llm, ...options } = config;
|
|
62
|
-
const results = await bulkMap(list, instructions, { chunkSize, llm, ...options });
|
|
63
|
-
for (let attempt = 1; attempt < maxAttempts; attempt += 1) {
|
|
64
|
-
const missingIdx = [];
|
|
65
|
-
const missingFragments = [];
|
|
66
|
-
results.forEach((val, idx) => {
|
|
67
|
-
if (val === undefined) {
|
|
68
|
-
missingIdx.push(idx);
|
|
69
|
-
missingFragments.push(list[idx]);
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
if (missingFragments.length === 0) break;
|
|
73
|
-
// eslint-disable-next-line no-await-in-loop
|
|
74
|
-
const retryResults = await bulkMap(missingFragments, instructions, {
|
|
75
|
-
chunkSize,
|
|
76
|
-
llm,
|
|
77
|
-
...options,
|
|
78
|
-
});
|
|
79
|
-
retryResults.forEach((val, i) => {
|
|
80
|
-
results[missingIdx[i]] = val;
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
return results;
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
export default bulkMap;
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import bulkMap, { bulkMapRetry } from './index.js';
|
|
3
|
-
import listMap from '../../verblets/list-map/index.js';
|
|
4
|
-
|
|
5
|
-
vi.mock('../../verblets/list-map/index.js', () => ({
|
|
6
|
-
default: vi.fn(async (items, instructions) => {
|
|
7
|
-
if (items.includes('FAIL')) throw new Error('fail');
|
|
8
|
-
return items.map((i) => `${i}-${instructions}`);
|
|
9
|
-
}),
|
|
10
|
-
}));
|
|
11
|
-
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
vi.clearAllMocks();
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
describe('bulkmap', () => {
|
|
17
|
-
it('maps fragments in batches', async () => {
|
|
18
|
-
const result = await bulkMap(['a', 'b', 'c'], 'x', { chunkSize: 2 });
|
|
19
|
-
expect(result).toStrictEqual(['a-x', 'b-x', 'c-x']);
|
|
20
|
-
expect(listMap).toHaveBeenCalledTimes(2);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('leaves undefined on error', async () => {
|
|
24
|
-
listMap.mockRejectedValueOnce(new Error('fail'));
|
|
25
|
-
const result = await bulkMap(['FAIL', 'oops'], 'x', { chunkSize: 2 });
|
|
26
|
-
expect(result).toStrictEqual([undefined, undefined]);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it('retries only failed fragments', async () => {
|
|
30
|
-
let call = 0;
|
|
31
|
-
listMap.mockImplementation(async (items) => {
|
|
32
|
-
call += 1;
|
|
33
|
-
if (call === 1) throw new Error('fail');
|
|
34
|
-
return items.map((l) => l.toUpperCase());
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
const result = await bulkMapRetry(['alpha', 'beta'], 'upper', {
|
|
38
|
-
chunkSize: 2,
|
|
39
|
-
maxAttempts: 2,
|
|
40
|
-
});
|
|
41
|
-
expect(result).toStrictEqual(['ALPHA', 'BETA']);
|
|
42
|
-
expect(listMap).toHaveBeenCalledTimes(2);
|
|
43
|
-
});
|
|
44
|
-
});
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
# bulk-reduce
|
|
2
|
-
|
|
3
|
-
Reduce long lists by processing them in smaller batches. Each batch is combined
|
|
4
|
-
with the accumulated result using `listReduce`.
|
|
5
|
-
|
|
6
|
-
```javascript
|
|
7
|
-
import bulkReduce from './index.js';
|
|
8
|
-
|
|
9
|
-
const logs = ['step one', 'step two', 'step three'];
|
|
10
|
-
const result = await bulkReduce(logs, 'summarize');
|
|
11
|
-
// => 'summary of steps'
|
|
12
|
-
```
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import bulkReduce from './index.js';
|
|
3
|
-
import { longTestTimeout } from '../../constants/common.js';
|
|
4
|
-
|
|
5
|
-
describe('bulk-reduce examples', () => {
|
|
6
|
-
it(
|
|
7
|
-
'reduces a long list sequentially',
|
|
8
|
-
async () => {
|
|
9
|
-
const items = ['one', 'two', 'three', 'four'];
|
|
10
|
-
const result = await bulkReduce(items, 'concatenate', { chunkSize: 2 });
|
|
11
|
-
expect(result).toBeDefined();
|
|
12
|
-
},
|
|
13
|
-
longTestTimeout
|
|
14
|
-
);
|
|
15
|
-
});
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import listReduce from '../../verblets/list-reduce/index.js';
|
|
2
|
-
|
|
3
|
-
export default async function bulkReduce(list, instructions, config = {}) {
|
|
4
|
-
const { chunkSize = 10, initial, llm, ...options } = config;
|
|
5
|
-
let acc = initial;
|
|
6
|
-
for (let i = 0; i < list.length; i += chunkSize) {
|
|
7
|
-
const batch = list.slice(i, i + chunkSize);
|
|
8
|
-
|
|
9
|
-
// eslint-disable-next-line no-await-in-loop
|
|
10
|
-
acc = await listReduce(acc, batch, instructions, { llm, ...options });
|
|
11
|
-
}
|
|
12
|
-
return acc;
|
|
13
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import bulkReduce from './index.js';
|
|
3
|
-
import listReduce from '../../verblets/list-reduce/index.js';
|
|
4
|
-
|
|
5
|
-
vi.mock('../../verblets/list-reduce/index.js', () => ({
|
|
6
|
-
default: vi.fn(async (acc, list) => [acc, ...list].filter(Boolean).join('-')),
|
|
7
|
-
}));
|
|
8
|
-
|
|
9
|
-
beforeEach(() => {
|
|
10
|
-
vi.clearAllMocks();
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
describe('bulk-reduce chain', () => {
|
|
14
|
-
it('reduces in batches', async () => {
|
|
15
|
-
const result = await bulkReduce(['a', 'b', 'c', 'd'], 'join', { chunkSize: 2 });
|
|
16
|
-
expect(result).toBe('a-b-c-d');
|
|
17
|
-
expect(listReduce).toHaveBeenCalledTimes(2);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('uses initial value', async () => {
|
|
21
|
-
const result = await bulkReduce(['x', 'y'], 'join', { initial: '0', chunkSize: 2 });
|
|
22
|
-
expect(result).toBe('0-x-y');
|
|
23
|
-
expect(listReduce).toHaveBeenCalledTimes(1);
|
|
24
|
-
});
|
|
25
|
-
});
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# bulk-score
|
|
2
|
-
|
|
3
|
-
Score lines of text on a 0–10 scale with automatic calibration. Each batch returns a JSON array so parsing stays reliable even with long lists. The chain first scores everything, then rescors a few low, middle, and high examples to calibrate. Those references feed a second scoring pass so every item is ranked consistently using OpenAI's JSON schema enforcement.
|
|
4
|
-
|
|
5
|
-
```javascript
|
|
6
|
-
import bulkScore from './index.js';
|
|
7
|
-
|
|
8
|
-
const slogans = [
|
|
9
|
-
'Amazing deals every day!',
|
|
10
|
-
'Unlock a world of wonder',
|
|
11
|
-
'Buy stuff now',
|
|
12
|
-
];
|
|
13
|
-
|
|
14
|
-
const { scores } = await bulkScore(slogans, 'How catchy is this marketing slogan?');
|
|
15
|
-
// scores like [6, 9, 2]
|
|
16
|
-
```
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
-
"type": "object",
|
|
4
|
-
"properties": {
|
|
5
|
-
"scores": {
|
|
6
|
-
"type": "array",
|
|
7
|
-
"description": "Array of numeric scores corresponding to input items",
|
|
8
|
-
"items": {
|
|
9
|
-
"type": "number",
|
|
10
|
-
"minimum": 0,
|
|
11
|
-
"maximum": 10,
|
|
12
|
-
"description": "Score from 0 (worst) to 10 (best)"
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
},
|
|
16
|
-
"required": ["scores"],
|
|
17
|
-
"additionalProperties": false
|
|
18
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { longTestTimeout } from '../../constants/common.js';
|
|
3
|
-
import bulkScore from './index.js';
|
|
4
|
-
|
|
5
|
-
describe('bulkScore examples', () => {
|
|
6
|
-
it(
|
|
7
|
-
'ranks jokes by humor',
|
|
8
|
-
async () => {
|
|
9
|
-
const jokes = [
|
|
10
|
-
'Why did the chicken cross the road? To get to the other side!',
|
|
11
|
-
"Parallel lines have so much in common. It's a shame they'll never meet.",
|
|
12
|
-
"I told my computer I needed a break, and it said 'I'll go to sleep.'",
|
|
13
|
-
];
|
|
14
|
-
|
|
15
|
-
const { scores } = await bulkScore(jokes, 'How funny is this joke?');
|
|
16
|
-
|
|
17
|
-
expect(scores).toHaveLength(jokes.length);
|
|
18
|
-
scores.forEach((s) => expect(typeof s).toBe('number'));
|
|
19
|
-
},
|
|
20
|
-
longTestTimeout
|
|
21
|
-
);
|
|
22
|
-
});
|
|
@@ -1,133 +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 { onlyJSONArray } = 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 bulk score results
|
|
16
|
-
* @returns {Promise<Object>} JSON schema for validation
|
|
17
|
-
*/
|
|
18
|
-
async function getBulkScoreSchema() {
|
|
19
|
-
const schemaPath = path.join(__dirname, 'bulk-score-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 getBulkScoreSchema();
|
|
30
|
-
|
|
31
|
-
const responseFormat = {
|
|
32
|
-
type: 'json_schema',
|
|
33
|
-
json_schema: {
|
|
34
|
-
name: 'bulk_score_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
|
-
async function scoreBatch(items, instructions, reference = [], config = {}) {
|
|
53
|
-
const { llm, ...options } = config;
|
|
54
|
-
const listBlock = wrapVariable(items.join('\n'), { tag: 'items' });
|
|
55
|
-
const refBlock = reference.length
|
|
56
|
-
? `\nCalibration examples (score - text):\n${wrapVariable(
|
|
57
|
-
reference.map((r) => `${r.score} - ${r.item}`).join('\n'),
|
|
58
|
-
{ tag: 'reference' }
|
|
59
|
-
)}`
|
|
60
|
-
: '';
|
|
61
|
-
|
|
62
|
-
const prompt =
|
|
63
|
-
`Score each line in <items> from 0 (worst) to 10 (best) based on: ${instructions}.` +
|
|
64
|
-
`\nRespond with a JSON object containing a "scores" array of numbers in the same order.` +
|
|
65
|
-
`${refBlock}\n${onlyJSONArray}\n${listBlock}`;
|
|
66
|
-
|
|
67
|
-
const modelOptions = await createModelOptions(llm);
|
|
68
|
-
const response = await chatGPT(prompt, {
|
|
69
|
-
modelOptions,
|
|
70
|
-
...options,
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
// With structured outputs, response should already be parsed and validated
|
|
74
|
-
const parsed = typeof response === 'string' ? JSON.parse(response) : response;
|
|
75
|
-
// Extract scores from the object structure
|
|
76
|
-
const arr = parsed?.scores || parsed;
|
|
77
|
-
|
|
78
|
-
if (!Array.isArray(arr) || arr.length !== items.length) {
|
|
79
|
-
throw new Error('Score batch mismatch');
|
|
80
|
-
}
|
|
81
|
-
return arr.map((n) => Number(n));
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export default async function bulkScore(list, instructions, config = {}) {
|
|
85
|
-
const { chunkSize = 10, examples, llm, ...options } = config;
|
|
86
|
-
if (!Array.isArray(list) || list.length === 0) {
|
|
87
|
-
return { scores: [], reference: [] };
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const firstScores = [];
|
|
91
|
-
for (let i = 0; i < list.length; i += chunkSize) {
|
|
92
|
-
// eslint-disable-next-line no-await-in-loop
|
|
93
|
-
const scores = await scoreBatch(list.slice(i, i + chunkSize), instructions, [], {
|
|
94
|
-
llm,
|
|
95
|
-
...options,
|
|
96
|
-
});
|
|
97
|
-
firstScores.push(...scores);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const scored = list.map((item, idx) => ({ item, score: firstScores[idx] }));
|
|
101
|
-
|
|
102
|
-
let reference = examples;
|
|
103
|
-
if (!reference) {
|
|
104
|
-
const valid = scored.filter((s) => Number.isFinite(s.score));
|
|
105
|
-
if (valid.length) {
|
|
106
|
-
valid.sort((a, b) => a.score - b.score);
|
|
107
|
-
const lows = valid.slice(0, 3);
|
|
108
|
-
const highs = valid.slice(-3);
|
|
109
|
-
const midStart = Math.max(0, Math.floor(valid.length / 2) - 1);
|
|
110
|
-
const mids = valid.slice(midStart, midStart + 3);
|
|
111
|
-
reference = [...lows, ...mids, ...highs];
|
|
112
|
-
const refItems = reference.map((r) => r.item);
|
|
113
|
-
const rescored = await scoreBatch(refItems, instructions, [], { llm, ...options });
|
|
114
|
-
rescored.forEach((score, idx) => {
|
|
115
|
-
reference[idx].score = score;
|
|
116
|
-
});
|
|
117
|
-
} else {
|
|
118
|
-
reference = [];
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const finalScores = [];
|
|
123
|
-
for (let i = 0; i < list.length; i += chunkSize) {
|
|
124
|
-
// eslint-disable-next-line no-await-in-loop
|
|
125
|
-
const scores = await scoreBatch(list.slice(i, i + chunkSize), instructions, reference, {
|
|
126
|
-
llm,
|
|
127
|
-
...options,
|
|
128
|
-
});
|
|
129
|
-
finalScores.push(...scores);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return { scores: finalScores, reference };
|
|
133
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import bulkScore from './index.js';
|
|
3
|
-
import chatGPT from '../../lib/chatgpt/index.js';
|
|
4
|
-
|
|
5
|
-
vi.mock('../../lib/chatgpt/index.js', () => ({
|
|
6
|
-
default: vi.fn(),
|
|
7
|
-
}));
|
|
8
|
-
|
|
9
|
-
beforeEach(() => {
|
|
10
|
-
vi.clearAllMocks();
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
describe('bulkScore chain', () => {
|
|
14
|
-
it('scores items using two passes', async () => {
|
|
15
|
-
chatGPT
|
|
16
|
-
.mockResolvedValueOnce('[1,2,3]')
|
|
17
|
-
.mockResolvedValueOnce('[1,2,3,4,5,6,7,8,9]')
|
|
18
|
-
.mockResolvedValueOnce('[1,2,3]');
|
|
19
|
-
const { scores, reference } = await bulkScore(['a', 'bb', 'ccc'], 'length');
|
|
20
|
-
expect(scores).toStrictEqual([1, 2, 3]);
|
|
21
|
-
expect(reference.length).toBeGreaterThan(0);
|
|
22
|
-
expect(chatGPT).toHaveBeenCalled();
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('uses provided examples', async () => {
|
|
26
|
-
chatGPT.mockResolvedValueOnce('[1]').mockResolvedValueOnce('[1]');
|
|
27
|
-
const { scores } = await bulkScore(['x'], 'length', { examples: [{ item: 'y', score: 2 }] });
|
|
28
|
-
expect(scores[0]).toBe(1);
|
|
29
|
-
});
|
|
30
|
-
});
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
# Category Samples Chain
|
|
2
|
-
|
|
3
|
-
Generate diverse, representative examples for any category. This chain applies prototype theory and related cognitive science principles to output a well-rounded set of sample items.
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- **Cognitive Science Foundation**: Uses prototype theory and family resemblance principles
|
|
8
|
-
- **Diversity Control**: Configurable diversity levels (focused, balanced, high)
|
|
9
|
-
- **Context Awareness**: Supports contextual constraints for targeted generation
|
|
10
|
-
- **Robust Retry Logic**: Built-in retry mechanisms for reliable generation
|
|
11
|
-
- **Scalable Architecture**: Leverages the list chain infrastructure for efficient processing
|
|
12
|
-
|
|
13
|
-
## Usage
|
|
14
|
-
|
|
15
|
-
### Basic Usage
|
|
16
|
-
|
|
17
|
-
```javascript
|
|
18
|
-
import categorySamples from './src/chains/category-samples/index.js';
|
|
19
|
-
|
|
20
|
-
// Generate basic fruit samples
|
|
21
|
-
const fruitSamples = await categorySamples('fruit', {
|
|
22
|
-
count: 5,
|
|
23
|
-
diversityLevel: 'balanced'
|
|
24
|
-
});
|
|
25
|
-
// Result: ['apple', 'orange', 'durian', 'banana', 'kiwi']
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
### With Context
|
|
29
|
-
|
|
30
|
-
```javascript
|
|
31
|
-
// Generate contextually relevant samples
|
|
32
|
-
const birdSamples = await categorySamples('bird', {
|
|
33
|
-
context: 'Common backyard birds in North America',
|
|
34
|
-
count: 4,
|
|
35
|
-
diversityLevel: 'focused'
|
|
36
|
-
});
|
|
37
|
-
// Result: ['robin', 'cardinal', 'blue jay', 'sparrow']
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
### High Diversity Generation
|
|
41
|
-
|
|
42
|
-
```javascript
|
|
43
|
-
// Generate diverse vehicle types
|
|
44
|
-
const vehicleSamples = await categorySamples('vehicle', {
|
|
45
|
-
count: 6,
|
|
46
|
-
diversityLevel: 'high'
|
|
47
|
-
});
|
|
48
|
-
// Result: ['car', 'bicycle', 'helicopter', 'submarine', 'skateboard', 'spaceship']
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
## API Reference
|
|
52
|
-
|
|
53
|
-
### `categorySamples(categoryName, options)`
|
|
54
|
-
|
|
55
|
-
Returns an array of sample items for the given category. Options let you control diversity, add context, and configure retry logic.
|
|
56
|
-
|
|
57
|
-
**Common Options**
|
|
58
|
-
|
|
59
|
-
- `count` (number): How many samples to return (default: 10)
|
|
60
|
-
- `context` (string): Extra context to guide generation
|
|
61
|
-
- `diversityLevel` ('focused' | 'balanced' | 'high'): Adjusts how typical or atypical the samples are
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { longTestTimeout } from '../../constants/common.js';
|
|
3
|
-
import { expect as aiExpect } from '../expect/index.js';
|
|
4
|
-
import categorySamples from './index.js';
|
|
5
|
-
|
|
6
|
-
describe('Category Samples Chain', () => {
|
|
7
|
-
it(
|
|
8
|
-
'generates basic seed items for a category',
|
|
9
|
-
async () => {
|
|
10
|
-
const seeds = await categorySamples('fruit', {
|
|
11
|
-
count: 5,
|
|
12
|
-
diversityLevel: 'balanced',
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
expect(seeds).toHaveLength(5);
|
|
16
|
-
expect(seeds.every((seed) => typeof seed === 'string')).toBe(true);
|
|
17
|
-
expect(seeds.every((seed) => seed.length > 0)).toBe(true);
|
|
18
|
-
|
|
19
|
-
// Use expect-chain for loose verification
|
|
20
|
-
const [isValidFruitList] = await aiExpect(
|
|
21
|
-
seeds,
|
|
22
|
-
undefined,
|
|
23
|
-
'Are these reasonable fruit names that represent a balanced mix of typical and moderately typical fruits?'
|
|
24
|
-
);
|
|
25
|
-
expect(isValidFruitList).toBe(true);
|
|
26
|
-
},
|
|
27
|
-
longTestTimeout
|
|
28
|
-
);
|
|
29
|
-
|
|
30
|
-
it(
|
|
31
|
-
'generates seeds with context',
|
|
32
|
-
async () => {
|
|
33
|
-
const seeds = await categorySamples('bird', {
|
|
34
|
-
context: 'Common backyard birds in North America',
|
|
35
|
-
count: 4,
|
|
36
|
-
diversityLevel: 'focused',
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
expect(seeds).toHaveLength(4);
|
|
40
|
-
expect(seeds.every((seed) => typeof seed === 'string')).toBe(true);
|
|
41
|
-
|
|
42
|
-
// Use expect-chain for loose verification
|
|
43
|
-
const [isValidBirdList] = await aiExpect(
|
|
44
|
-
seeds,
|
|
45
|
-
undefined,
|
|
46
|
-
'Are these reasonable names of common backyard birds that would be found in North America?'
|
|
47
|
-
);
|
|
48
|
-
expect(isValidBirdList).toBe(true);
|
|
49
|
-
},
|
|
50
|
-
longTestTimeout
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
it(
|
|
54
|
-
'generates diverse seeds with high diversity level',
|
|
55
|
-
async () => {
|
|
56
|
-
const seeds = await categorySamples('vehicle', {
|
|
57
|
-
count: 6,
|
|
58
|
-
diversityLevel: 'high',
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
expect(seeds).toHaveLength(6);
|
|
62
|
-
expect(seeds.every((seed) => typeof seed === 'string')).toBe(true);
|
|
63
|
-
|
|
64
|
-
// Use expect-chain for loose verification - check for diversity without being overly specific
|
|
65
|
-
const [isValidVehicleList] = await aiExpect(
|
|
66
|
-
seeds,
|
|
67
|
-
undefined,
|
|
68
|
-
'Are these vehicle names reasonably diverse, showing variety in the types of vehicles represented?'
|
|
69
|
-
);
|
|
70
|
-
expect(isValidVehicleList).toBe(true);
|
|
71
|
-
},
|
|
72
|
-
longTestTimeout
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
it('throws error for invalid category name', async () => {
|
|
76
|
-
await expect(categorySamples('')).rejects.toThrow('categoryName must be a non-empty string');
|
|
77
|
-
await expect(categorySamples(null)).rejects.toThrow('categoryName must be a non-empty string');
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it(
|
|
81
|
-
'handles retry logic on failures',
|
|
82
|
-
async () => {
|
|
83
|
-
// This test ensures the retry mechanism works
|
|
84
|
-
const seeds = await categorySamples('animal', {
|
|
85
|
-
count: 3,
|
|
86
|
-
maxRetries: 2,
|
|
87
|
-
retryDelay: 100,
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
expect(seeds).toHaveLength(3);
|
|
91
|
-
expect(seeds.every((seed) => typeof seed === 'string')).toBe(true);
|
|
92
|
-
|
|
93
|
-
// Use expect-chain for loose verification
|
|
94
|
-
const [isValidAnimalList] = await aiExpect(
|
|
95
|
-
seeds,
|
|
96
|
-
undefined,
|
|
97
|
-
'Are these reasonable animal names?'
|
|
98
|
-
);
|
|
99
|
-
expect(isValidAnimalList).toBe(true);
|
|
100
|
-
},
|
|
101
|
-
longTestTimeout
|
|
102
|
-
);
|
|
103
|
-
});
|