@far-world-labs/verblets 0.1.1 → 0.1.3

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.
Files changed (300) hide show
  1. package/.cursor/launch.json +30 -0
  2. package/.cursor/settings.json +20 -0
  3. package/.github/workflows/branch-protection.yml +22 -0
  4. package/.github/workflows/ci.yml +120 -0
  5. package/.prettierrc +6 -0
  6. package/.release-it.json +4 -1
  7. package/.vscode/launch.json +31 -0
  8. package/AGENTS.md +220 -0
  9. package/DEVELOPING.md +105 -0
  10. package/README.md +254 -0
  11. package/eslint.config.js +80 -0
  12. package/package.json +29 -17
  13. package/scripts/generate-test/index.js +29 -3
  14. package/scripts/runner/index.js +26 -0
  15. package/scripts/simple-editor/index.js +29 -18
  16. package/scripts/summarize-files/index.js +28 -4
  17. package/src/chains/README.md +30 -0
  18. package/src/chains/anonymize/README.md +21 -0
  19. package/src/chains/anonymize/index.examples.js +75 -0
  20. package/src/chains/anonymize/index.js +121 -0
  21. package/src/chains/anonymize/index.spec.js +78 -0
  22. package/src/chains/bulk-central-tendency/index.examples.js +138 -0
  23. package/src/chains/bulk-central-tendency/index.js +91 -0
  24. package/src/chains/bulk-filter/README.md +21 -0
  25. package/src/chains/bulk-filter/index.examples.js +22 -0
  26. package/src/chains/bulk-filter/index.js +58 -0
  27. package/src/chains/bulk-filter/index.spec.js +38 -0
  28. package/src/chains/bulk-find/README.md +16 -0
  29. package/src/chains/bulk-find/index.examples.js +20 -0
  30. package/src/chains/bulk-find/index.js +30 -0
  31. package/src/chains/bulk-find/index.spec.js +26 -0
  32. package/src/chains/bulk-group/README.md +23 -0
  33. package/src/chains/bulk-group/index.examples.js +18 -0
  34. package/src/chains/bulk-group/index.js +34 -0
  35. package/src/chains/bulk-group/index.spec.js +41 -0
  36. package/src/chains/bulk-map/README.md +43 -0
  37. package/src/chains/bulk-map/index.examples.js +17 -0
  38. package/src/chains/bulk-map/index.js +86 -0
  39. package/src/chains/bulk-map/index.spec.js +44 -0
  40. package/src/chains/bulk-reduce/README.md +12 -0
  41. package/src/chains/bulk-reduce/index.examples.js +15 -0
  42. package/src/chains/bulk-reduce/index.js +13 -0
  43. package/src/chains/bulk-reduce/index.spec.js +25 -0
  44. package/src/chains/bulk-score/README.md +16 -0
  45. package/src/chains/bulk-score/bulk-score-result.json +18 -0
  46. package/src/chains/bulk-score/index.examples.js +22 -0
  47. package/src/chains/bulk-score/index.js +133 -0
  48. package/src/chains/bulk-score/index.spec.js +30 -0
  49. package/src/chains/category-samples/README.md +61 -0
  50. package/src/chains/category-samples/index.examples.js +103 -0
  51. package/src/chains/category-samples/index.js +134 -0
  52. package/src/chains/collect-terms/README.md +12 -0
  53. package/src/chains/collect-terms/index.examples.js +16 -0
  54. package/src/chains/collect-terms/index.js +44 -0
  55. package/src/chains/collect-terms/index.spec.js +25 -0
  56. package/src/chains/date/README.md +12 -0
  57. package/src/chains/date/index.examples.js +47 -0
  58. package/src/chains/date/index.js +74 -0
  59. package/src/chains/date/index.spec.js +62 -0
  60. package/src/chains/disambiguate/README.md +22 -0
  61. package/src/chains/disambiguate/disambiguate-meanings-result.json +16 -0
  62. package/src/chains/disambiguate/index.examples.js +18 -0
  63. package/src/chains/disambiguate/index.js +92 -0
  64. package/src/chains/disambiguate/index.spec.js +25 -0
  65. package/src/chains/dismantle/README.md +67 -0
  66. package/src/chains/dismantle/dismantle.examples.js +27 -0
  67. package/src/chains/dismantle/index.js +6 -17
  68. package/src/chains/dismantle/index.spec.js +1 -2
  69. package/src/chains/expect/README.md +171 -0
  70. package/src/chains/expect/index.examples.js +146 -0
  71. package/src/chains/expect/index.js +173 -0
  72. package/src/chains/expect/index.spec.js +324 -0
  73. package/src/chains/filter-ambiguous/README.md +11 -0
  74. package/src/chains/filter-ambiguous/index.examples.js +20 -0
  75. package/src/chains/filter-ambiguous/index.js +49 -0
  76. package/src/chains/filter-ambiguous/index.spec.js +31 -0
  77. package/src/chains/glossary/README.md +19 -0
  78. package/src/chains/glossary/index.examples.js +386 -0
  79. package/src/chains/glossary/index.js +75 -0
  80. package/src/chains/glossary/index.spec.js +19 -0
  81. package/src/chains/intersections/README.md +152 -0
  82. package/src/chains/intersections/index.examples.js +279 -0
  83. package/src/chains/intersections/index.js +366 -0
  84. package/src/chains/intersections/intersection-result.json +38 -0
  85. package/src/chains/list/index.examples.js +12 -16
  86. package/src/chains/list/index.js +106 -53
  87. package/src/chains/list/index.spec.js +8 -9
  88. package/src/chains/list/list-result.json +16 -0
  89. package/src/chains/llm-logger/README.md +208 -0
  90. package/src/chains/llm-logger/index.js +205 -0
  91. package/src/chains/llm-logger/index.spec.js +330 -0
  92. package/src/chains/questions/index.examples.js +2 -1
  93. package/src/chains/questions/index.js +14 -15
  94. package/src/chains/scan-js/index.js +6 -9
  95. package/src/chains/set-interval/README.md +81 -0
  96. package/src/chains/set-interval/index.examples.js +36 -0
  97. package/src/chains/set-interval/index.js +131 -0
  98. package/src/chains/set-interval/index.spec.js +70 -0
  99. package/src/chains/socratic/README.md +17 -0
  100. package/src/chains/socratic/index.js +64 -0
  101. package/src/chains/socratic/index.spec.js +24 -0
  102. package/src/chains/sort/index.examples.js +3 -7
  103. package/src/chains/sort/index.js +65 -15
  104. package/src/chains/sort/index.spec.js +5 -8
  105. package/src/chains/sort/sort-result.json +16 -0
  106. package/src/chains/summary-map/README.md +9 -1
  107. package/src/chains/summary-map/index.examples.js +9 -2
  108. package/src/chains/summary-map/index.js +43 -25
  109. package/src/chains/summary-map/index.spec.js +78 -3
  110. package/src/chains/test/index.js +9 -13
  111. package/src/chains/test-advice/index.js +4 -5
  112. package/src/chains/themes/README.md +20 -0
  113. package/src/chains/themes/index.examples.js +17 -0
  114. package/src/chains/themes/index.js +28 -0
  115. package/src/chains/themes/index.spec.js +19 -0
  116. package/src/chains/veiled-variants/index.examples.js +18 -0
  117. package/src/chains/veiled-variants/index.js +107 -0
  118. package/src/chains/veiled-variants/index.spec.js +40 -0
  119. package/src/constants/common.js +0 -2
  120. package/src/constants/models.js +172 -0
  121. package/src/index.js +178 -18
  122. package/src/json-schemas/README.md +13 -0
  123. package/src/json-schemas/index.js +8 -14
  124. package/src/json-schemas/schema-dot-org-photograph.json +11 -5
  125. package/src/json-schemas/schema-dot-org-place.json +78 -5
  126. package/src/lib/README.md +26 -0
  127. package/src/lib/bulk-filter/README.md +22 -0
  128. package/src/lib/bulk-filter/index.examples.js +27 -0
  129. package/src/lib/bulk-filter/index.js +63 -0
  130. package/src/lib/bulk-filter/index.spec.js +38 -0
  131. package/src/lib/bulk-find/README.md +18 -0
  132. package/src/lib/bulk-find/index.examples.js +19 -0
  133. package/src/lib/bulk-find/index.js +30 -0
  134. package/src/lib/bulk-find/index.spec.js +41 -0
  135. package/src/lib/chatgpt/index.js +63 -43
  136. package/src/lib/combinations/index.js +30 -0
  137. package/src/lib/combinations/index.spec.js +23 -0
  138. package/src/lib/functional/index.js +28 -0
  139. package/src/lib/logger-service/index.js +32 -0
  140. package/src/lib/parse-js-parts/index.js +9 -21
  141. package/src/lib/parse-llm-list/README.md +39 -0
  142. package/src/lib/parse-llm-list/index.js +54 -0
  143. package/src/lib/parse-llm-list/index.spec.js +59 -0
  144. package/src/lib/path-aliases/index.js +1 -3
  145. package/src/lib/path-aliases/index.spec.js +2 -8
  146. package/src/lib/pave/index.js +4 -4
  147. package/src/lib/pave/index.spec.js +6 -3
  148. package/src/lib/prompt-cache/index.js +14 -10
  149. package/src/lib/retry/index.js +11 -8
  150. package/src/lib/ring-buffer/README.md +460 -0
  151. package/src/lib/ring-buffer/index.js +1074 -0
  152. package/src/lib/search-best-first/city-walk.spec.js +37 -0
  153. package/src/lib/search-best-first/index.js +42 -11
  154. package/src/lib/search-best-first/index.spec.js +35 -0
  155. package/src/lib/search-js-files/index.js +44 -47
  156. package/src/lib/search-js-files/scan-file.js +10 -21
  157. package/src/lib/shorten-text/index.js +2 -7
  158. package/src/lib/shorten-text/index.spec.js +3 -3
  159. package/src/lib/strip-response/index.js +2 -7
  160. package/src/lib/template-replace/index.js +23 -0
  161. package/src/lib/template-replace/index.spec.js +60 -0
  162. package/src/lib/to-date/index.js +11 -0
  163. package/src/lib/to-number/index.js +1 -1
  164. package/src/lib/transcribe/index.js +26 -9
  165. package/src/prompts/README.md +3 -1
  166. package/src/prompts/as-object-with-schema.js +3 -8
  167. package/src/prompts/as-schema-org-text.js +10 -2
  168. package/src/prompts/code-features.js +1 -5
  169. package/src/prompts/constants.js +27 -27
  170. package/src/prompts/generate-collection.js +1 -1
  171. package/src/prompts/intent.js +16 -22
  172. package/src/prompts/select-from-threshold.js +1 -2
  173. package/src/prompts/sort.js +4 -8
  174. package/src/prompts/style.js +4 -7
  175. package/src/prompts/wrap-list.js +1 -4
  176. package/src/services/llm-model/global-overrides.spec.js +432 -0
  177. package/src/services/llm-model/index.js +234 -40
  178. package/src/services/llm-model/model.js +2 -2
  179. package/src/services/llm-model/negotiate.spec.js +447 -0
  180. package/src/services/redis/index.js +70 -7
  181. package/src/test/setup.js +20 -0
  182. package/src/verblets/README.md +26 -0
  183. package/src/verblets/auto/index.examples.js +12 -9
  184. package/src/verblets/auto/index.js +10 -10
  185. package/src/verblets/auto/index.spec.js +4 -6
  186. package/src/verblets/bool/README.md +36 -0
  187. package/src/verblets/bool/index.examples.js +53 -1
  188. package/src/verblets/bool/index.js +6 -9
  189. package/src/verblets/bool/index.spec.js +1 -3
  190. package/src/verblets/central-tendency/README.md +166 -0
  191. package/src/verblets/central-tendency/central-tendency-result.json +24 -0
  192. package/src/verblets/central-tendency/index.examples.js +196 -0
  193. package/src/verblets/central-tendency/index.js +171 -0
  194. package/src/verblets/central-tendency/index.spec.js +148 -0
  195. package/src/verblets/enum/index.examples.js +1 -4
  196. package/src/verblets/enum/index.js +7 -4
  197. package/src/verblets/expect/README.md +64 -0
  198. package/src/verblets/expect/index.examples.js +109 -0
  199. package/src/verblets/expect/index.js +75 -0
  200. package/src/verblets/expect/index.spec.js +127 -0
  201. package/src/verblets/intent/index.examples.js +95 -7
  202. package/src/verblets/intent/index.js +56 -68
  203. package/src/verblets/intersection/README.md +16 -0
  204. package/src/verblets/intersection/index.examples.js +89 -0
  205. package/src/verblets/intersection/index.js +84 -0
  206. package/src/verblets/intersection/index.spec.js +60 -0
  207. package/src/verblets/intersection/intersection-result.json +16 -0
  208. package/src/verblets/list-expand/README.md +10 -0
  209. package/src/verblets/list-expand/index.examples.js +14 -0
  210. package/src/verblets/list-expand/index.js +104 -0
  211. package/src/verblets/list-expand/index.spec.js +18 -0
  212. package/src/verblets/list-expand/list-expand-result.json +16 -0
  213. package/src/verblets/list-filter/README.md +22 -0
  214. package/src/verblets/list-filter/index.examples.js +26 -0
  215. package/src/verblets/list-filter/index.js +18 -0
  216. package/src/verblets/list-filter/index.spec.js +19 -0
  217. package/src/verblets/list-find/README.md +11 -0
  218. package/src/verblets/list-find/index.examples.js +15 -0
  219. package/src/verblets/list-find/index.js +17 -0
  220. package/src/verblets/list-find/index.spec.js +19 -0
  221. package/src/verblets/list-group/README.md +16 -0
  222. package/src/verblets/list-group/index.examples.js +16 -0
  223. package/src/verblets/list-group/index.js +112 -0
  224. package/src/verblets/list-group/index.spec.js +35 -0
  225. package/src/verblets/list-group/list-group-result.json +16 -0
  226. package/src/verblets/list-map/README.md +11 -0
  227. package/src/verblets/list-map/index.examples.js +15 -0
  228. package/src/verblets/list-map/index.js +26 -0
  229. package/src/verblets/list-map/index.spec.js +17 -0
  230. package/src/verblets/list-reduce/README.md +10 -0
  231. package/src/verblets/list-reduce/index.examples.js +14 -0
  232. package/src/verblets/list-reduce/index.js +21 -0
  233. package/src/verblets/list-reduce/index.spec.js +27 -0
  234. package/src/verblets/list-reduce/index.spec.jsx +27 -0
  235. package/src/verblets/name/README.md +15 -0
  236. package/src/verblets/name/index.examples.js +28 -0
  237. package/src/verblets/name/index.js +19 -0
  238. package/src/verblets/name/index.spec.js +33 -0
  239. package/src/verblets/name-similar-to/README.md +26 -0
  240. package/src/verblets/name-similar-to/index.examples.js +18 -0
  241. package/src/verblets/name-similar-to/index.js +20 -0
  242. package/src/verblets/name-similar-to/index.spec.js +13 -0
  243. package/src/verblets/number/index.examples.js +173 -7
  244. package/src/verblets/number/index.js +5 -2
  245. package/src/verblets/number/index.spec.js +1 -3
  246. package/src/verblets/number-with-units/index.examples.js +5 -1
  247. package/src/verblets/number-with-units/index.js +74 -9
  248. package/src/verblets/number-with-units/number-with-units-result.json +23 -0
  249. package/src/verblets/schema-org/index.examples.js +2 -7
  250. package/src/verblets/schema-org/index.js +32 -3
  251. package/src/verblets/sentiment/README.md +10 -0
  252. package/src/verblets/sentiment/index.examples.js +20 -0
  253. package/src/verblets/sentiment/index.js +9 -0
  254. package/src/verblets/sentiment/index.spec.js +20 -0
  255. package/src/verblets/to-object/index.js +10 -15
  256. package/src/verblets/to-object/index.spec.js +1 -4
  257. package/.eslintrc.json +0 -42
  258. package/docs/README.md +0 -41
  259. package/docs/babel.config.js +0 -3
  260. package/docs/blog/2019-05-28-first-blog-post.md +0 -12
  261. package/docs/blog/2019-05-29-long-blog-post.md +0 -44
  262. package/docs/blog/2021-08-01-mdx-blog-post.mdx +0 -20
  263. package/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg +0 -0
  264. package/docs/blog/2021-08-26-welcome/index.md +0 -25
  265. package/docs/blog/authors.yml +0 -17
  266. package/docs/docs/api/bool.md +0 -74
  267. package/docs/docs/api/search.md +0 -51
  268. package/docs/docs/intro.md +0 -47
  269. package/docs/docs/tutorial-basics/_category_.json +0 -8
  270. package/docs/docs/tutorial-basics/congratulations.md +0 -23
  271. package/docs/docs/tutorial-basics/create-a-blog-post.md +0 -34
  272. package/docs/docs/tutorial-basics/create-a-document.md +0 -57
  273. package/docs/docs/tutorial-basics/create-a-page.md +0 -43
  274. package/docs/docs/tutorial-basics/deploy-your-site.md +0 -31
  275. package/docs/docs/tutorial-basics/markdown-features.mdx +0 -152
  276. package/docs/docs/tutorial-extras/_category_.json +0 -7
  277. package/docs/docs/tutorial-extras/img/docsVersionDropdown.png +0 -0
  278. package/docs/docs/tutorial-extras/img/localeDropdown.png +0 -0
  279. package/docs/docs/tutorial-extras/manage-docs-versions.md +0 -55
  280. package/docs/docs/tutorial-extras/translate-your-site.md +0 -88
  281. package/docs/docusaurus.config.js +0 -120
  282. package/docs/package.json +0 -44
  283. package/docs/sidebars.js +0 -31
  284. package/docs/src/components/HomepageFeatures/index.js +0 -61
  285. package/docs/src/components/HomepageFeatures/styles.module.css +0 -11
  286. package/docs/src/css/custom.css +0 -30
  287. package/docs/src/pages/index.js +0 -43
  288. package/docs/src/pages/index.module.css +0 -23
  289. package/docs/src/pages/markdown-page.md +0 -7
  290. package/docs/static/.nojekyll +0 -0
  291. package/docs/static/img/docusaurus-social-card.jpg +0 -0
  292. package/docs/static/img/docusaurus.png +0 -0
  293. package/docs/static/img/favicon.ico +0 -0
  294. package/docs/static/img/logo.svg +0 -1
  295. package/docs/static/img/undraw_docusaurus_mountain.svg +0 -171
  296. package/docs/static/img/undraw_docusaurus_react.svg +0 -170
  297. package/docs/static/img/undraw_docusaurus_tree.svg +0 -40
  298. package/src/constants/openai.js +0 -65
  299. /package/{.vite.config.examples.js → .vitest.config.examples.js} +0 -0
  300. /package/{.vite.config.js → .vitest.config.js} +0 -0
@@ -0,0 +1,61 @@
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
@@ -0,0 +1,103 @@
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
+ });
@@ -0,0 +1,134 @@
1
+ import list from '../list/index.js';
2
+ import retry from '../../lib/retry/index.js';
3
+ import modelService from '../../services/llm-model/index.js';
4
+
5
+ /**
6
+ * Core prompt template for sample generation using cognitive science principles
7
+ */
8
+ export const SAMPLE_GENERATION_PROMPT = `Generate sample items for the category "{categoryName}" using cognitive science principles.
9
+
10
+ {context}
11
+
12
+ COGNITIVE PRINCIPLES:
13
+ 1. Prototype Theory: Include items across the typicality spectrum
14
+ 2. Family Resemblance: Ensure items share overlapping but not identical features
15
+ 3. Category Structure: {diversityInstructions}
16
+
17
+ REQUIREMENTS:
18
+ - Include highly typical/prototypical members
19
+ - Include moderately typical members
20
+ - {diversityRequirement}
21
+ - Ensure good coverage of category space
22
+ - Avoid redundant or near-identical items
23
+
24
+ IMPORTANT: Return only clean item names without numbering, descriptions, or explanations.`;
25
+
26
+ /**
27
+ * Build sample generation prompt with specific parameters
28
+ * @param {string} categoryName - Name of the category
29
+ * @param {Object} config - Configuration options
30
+ * @returns {string} Complete prompt for sample generation
31
+ */
32
+ export function buildSeedGenerationPrompt(
33
+ categoryName,
34
+ { context = '', diversityLevel = 'balanced' } = {}
35
+ ) {
36
+ const diversityInstructions = {
37
+ high: 'Include very diverse examples spanning edge cases and borderline members',
38
+ balanced: 'Include a mix of typical, moderately typical, and some atypical members',
39
+ focused: 'Focus on highly typical, central members with clear category membership',
40
+ };
41
+
42
+ const diversityRequirement = {
43
+ high: 'Include many atypical but valid members',
44
+ balanced: 'Include some moderately atypical members',
45
+ focused: 'Focus primarily on typical members',
46
+ };
47
+
48
+ const contextLine = context ? `Context: ${context}` : '';
49
+
50
+ return SAMPLE_GENERATION_PROMPT.replace('{categoryName}', categoryName)
51
+ .replace('{context}', contextLine)
52
+ .replace(
53
+ '{diversityInstructions}',
54
+ diversityInstructions[diversityLevel] || diversityInstructions.balanced
55
+ )
56
+ .replace(
57
+ '{diversityRequirement}',
58
+ diversityRequirement[diversityLevel] || diversityRequirement.balanced
59
+ );
60
+ }
61
+
62
+ /**
63
+ * Generate sample items for a category using cognitive science principles.
64
+ * Creates diverse, representative examples across the typicality spectrum.
65
+ *
66
+ * @param {string} categoryName - Name of the category
67
+ * @param {Object} [options={}] - Configuration options
68
+ * @param {string} [options.context=''] - Context for sample generation
69
+ * @param {number} [options.count=30] - Number of sample items to generate
70
+ * @param {string} [options.diversityLevel='balanced'] - 'high', 'balanced', or 'focused'
71
+ * @param {string|Object} [options.llm='fastGoodCheap'] - LLM model to use
72
+ * @param {number} [options.maxRetries=3] - Maximum number of retry attempts
73
+ * @param {number} [options.retryDelay=1000] - Delay between retries in milliseconds
74
+ * @returns {Promise<string[]>}
75
+ */
76
+ export default async function categorySamples(categoryName, options = {}) {
77
+ if (!categoryName || typeof categoryName !== 'string') {
78
+ throw new Error('categoryName must be a non-empty string');
79
+ }
80
+
81
+ const {
82
+ context = '',
83
+ count = 30,
84
+ diversityLevel = 'balanced',
85
+ llm = 'fastGoodCheap',
86
+ maxRetries = 3,
87
+ retryDelay = 1000,
88
+ } = options;
89
+
90
+ const generateWithRetry = async () => {
91
+ const prompt = buildSeedGenerationPrompt(categoryName, { context, diversityLevel });
92
+
93
+ // Get the model object from the model service
94
+ const model = typeof llm === 'string' ? modelService.getModel(llm) : llm;
95
+
96
+ const results = await list(prompt, {
97
+ llm: model,
98
+ shouldStop: ({ resultsAll }) => resultsAll.length >= count,
99
+ });
100
+
101
+ if (!results || results.length === 0) {
102
+ throw new Error(`No sample items generated for category: ${categoryName}`);
103
+ }
104
+
105
+ // Return only the requested count
106
+ return results.slice(0, count);
107
+ };
108
+
109
+ return await retry(generateWithRetry, {
110
+ maxRetries,
111
+ retryDelay,
112
+ retryCondition: (error) => {
113
+ // Retry on network errors, timeouts, or empty results
114
+ return (
115
+ error.message.includes('No sample items generated') ||
116
+ error.message.includes('timeout') ||
117
+ error.message.includes('network') ||
118
+ error.message.includes('ECONNRESET')
119
+ );
120
+ },
121
+ });
122
+ }
123
+
124
+ /**
125
+ * Generate sample items for a category using list generation
126
+ * @param {string} category - The category to generate samples for
127
+ * @param {number} _count - Target number of samples (unused, kept for API compatibility)
128
+ * @param {Object} options - Additional options
129
+ * @returns {Promise<string[]>} Array of sample items
130
+ */
131
+ export function categorySamplesList(category, _count = 10, options = {}) {
132
+ // Use the list chain to generate samples for the category
133
+ return list(category, options);
134
+ }
@@ -0,0 +1,12 @@
1
+ # collect-terms
2
+
3
+ Extract the most difficult or technical terms from any passage. Useful for building a glossary or highlighting vocabulary that needs clarification.
4
+
5
+ ## Usage
6
+
7
+ ```javascript
8
+ import collectTerms from './collect-terms/index.js';
9
+
10
+ const terms = await collectTerms(longText, { topN: 15 });
11
+ // => ['usufructuary rights', 'riparian', 'hydrological cycle', ...]
12
+ ```
@@ -0,0 +1,16 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { longTestTimeout } from '../../constants/common.js';
3
+ import collectTerms from './index.js';
4
+
5
+ const sample = `Quantum entanglement and the observer effect are central to modern physics. Understanding Heisenberg's uncertainty principle alongside quantum decoherence helps explain particle behavior at subatomic scales.`;
6
+
7
+ describe('collectTerms chain', () => {
8
+ it(
9
+ 'Example',
10
+ async () => {
11
+ const terms = await collectTerms(sample, { topN: 5 });
12
+ expect(Array.isArray(terms)).toBe(true);
13
+ },
14
+ longTestTimeout
15
+ );
16
+ });
@@ -0,0 +1,44 @@
1
+ import list from '../list/index.js';
2
+ import listReduce from '../../verblets/list-reduce/index.js';
3
+
4
+ const splitIntoChunks = (text, maxLen) => {
5
+ const words = text.split(/\s+/);
6
+ const chunks = [];
7
+ let current = '';
8
+ for (const word of words) {
9
+ if (current.length + word.length + 1 > maxLen) {
10
+ if (current) chunks.push(current.trim());
11
+ current = word;
12
+ } else {
13
+ current += (current ? ' ' : '') + word;
14
+ }
15
+ }
16
+ if (current) chunks.push(current.trim());
17
+ return chunks;
18
+ };
19
+
20
+ export default async function collectTerms(text, config = {}) {
21
+ const { chunkLen = 1000, topN = 20, llm, ...options } = config;
22
+ const chunks = splitIntoChunks(text, chunkLen);
23
+ const allTerms = [];
24
+ for (const chunk of chunks) {
25
+ const terms = await list(`important complex or technical terms from: ${chunk}`, {
26
+ llm,
27
+ ...options,
28
+ });
29
+ allTerms.push(...terms);
30
+ }
31
+ const uniqueTerms = Array.from(new Set(allTerms.map((t) => t.trim())));
32
+ if (uniqueTerms.length <= topN) return uniqueTerms;
33
+ const reduced = await listReduce(
34
+ '',
35
+ uniqueTerms,
36
+ `Return the top ${topN} terms as a comma-separated list`,
37
+ { llm, ...options }
38
+ );
39
+ return reduced
40
+ .split(',')
41
+ .map((t) => t.trim())
42
+ .filter(Boolean)
43
+ .slice(0, topN);
44
+ }
@@ -0,0 +1,25 @@
1
+ import { describe, expect, it, beforeEach, vi } from 'vitest';
2
+ import collectTerms from './index.js';
3
+ import list from '../list/index.js';
4
+ import listReduce from '../../verblets/list-reduce/index.js';
5
+
6
+ vi.mock('../list/index.js');
7
+ vi.mock('../../verblets/list-reduce/index.js');
8
+
9
+ beforeEach(() => {
10
+ vi.resetAllMocks();
11
+ });
12
+
13
+ describe('collectTerms chain', () => {
14
+ it('deduplicates and reduces to top terms', async () => {
15
+ list.mockResolvedValueOnce(['alpha', 'beta']).mockResolvedValueOnce(['beta', 'gamma']);
16
+ listReduce.mockResolvedValue('alpha, beta, gamma');
17
+
18
+ const text = 'p1\n\np2';
19
+ const result = await collectTerms(text, { chunkLen: 2, topN: 2 });
20
+
21
+ expect(list).toHaveBeenCalledTimes(2);
22
+ expect(result).toStrictEqual(['alpha', 'beta']);
23
+ expect(listReduce).toHaveBeenCalled();
24
+ });
25
+ });
@@ -0,0 +1,12 @@
1
+ # date
2
+
3
+ Iteratively refine LLM answers until they produce a valid JavaScript `Date` object that satisfies the prompt. Before asking for a date, the chain asks the language model to suggest a few quick checks that a correct answer should satisfy. Each returned date is evaluated against those expectations with the `bool` verblt and retried if any fail.
4
+
5
+ ```javascript
6
+ import date from './index.js';
7
+
8
+ const release = await date('When was the original Star Wars film released?');
9
+ // => new Date('1977-05-25')
10
+ ```
11
+
12
+ The chain asks for a date using prompt constants shared with primitive verblets. It then verifies the result with the `bool` verblet and retries until the date is deemed correct or attempts run out.
@@ -0,0 +1,47 @@
1
+ import { describe, expect, it, beforeAll, afterAll } from 'vitest';
2
+ import date from './index.js';
3
+ import { expect as llmExpect } from '../../chains/expect/index.js';
4
+ import { longTestTimeout } from '../../constants/common.js';
5
+
6
+ describe('date examples', () => {
7
+ const originalMode = process.env.LLM_EXPECT_MODE;
8
+
9
+ beforeAll(() => {
10
+ process.env.LLM_EXPECT_MODE = 'none';
11
+ });
12
+
13
+ afterAll(() => {
14
+ if (originalMode !== undefined) {
15
+ process.env.LLM_EXPECT_MODE = originalMode;
16
+ } else {
17
+ delete process.env.LLM_EXPECT_MODE;
18
+ }
19
+ });
20
+
21
+ it(
22
+ 'gets Star Wars release date',
23
+ async () => {
24
+ const result = await date('When was the original Star Wars released?');
25
+ expect(result instanceof Date).toBe(true);
26
+ const [reasonable] = await llmExpect(
27
+ `Star Wars release date: ${result.toISOString()}`,
28
+ undefined,
29
+ 'Is this close to the actual release date of the first Star Wars movie?'
30
+ );
31
+ expect(reasonable).toBe(true);
32
+ },
33
+ longTestTimeout
34
+ );
35
+
36
+ it(
37
+ 'finds Christmas 2025',
38
+ async () => {
39
+ const result = await date('When is Christmas Day in 2025?');
40
+ expect(result instanceof Date).toBe(true);
41
+ expect(result.getUTCFullYear()).toBe(2025);
42
+ expect(result.getUTCMonth()).toBe(11); // December
43
+ expect(result.getUTCDate()).toBe(25);
44
+ },
45
+ longTestTimeout
46
+ );
47
+ });
@@ -0,0 +1,74 @@
1
+ import chatGPT from '../../lib/chatgpt/index.js';
2
+ import stripResponse from '../../lib/strip-response/index.js';
3
+ import toDate from '../../lib/to-date/index.js';
4
+ import toObject from '../../verblets/to-object/index.js';
5
+ import bool from '../../verblets/bool/index.js';
6
+ import { constants as promptConstants } from '../../prompts/index.js';
7
+
8
+ const {
9
+ asDate,
10
+ asUndefinedByDefault,
11
+ contentIsQuestion,
12
+ explainAndSeparate,
13
+ explainAndSeparatePrimitive,
14
+ onlyJSONArray,
15
+ } = promptConstants;
16
+
17
+ const expectationPrompt = (question) => `${contentIsQuestion} ${question}
18
+
19
+ List up to three short yes/no checks that would confirm a date answer is correct. If nothing specific comes to mind, respond with ["The result is a valid date"].
20
+
21
+ ${onlyJSONArray}`;
22
+
23
+ const buildCheckPrompt = (dateValue, check) => {
24
+ const iso = dateValue.toISOString();
25
+ const human = dateValue.toUTCString();
26
+ const utcDate = new Date(
27
+ Date.UTC(dateValue.getUTCFullYear(), dateValue.getUTCMonth(), dateValue.getUTCDate())
28
+ );
29
+ return `Date in ISO: ${iso} (UTC: ${human}, UTC date: ${utcDate.toISOString()}). Does this satisfy "${check}"?`;
30
+ };
31
+
32
+ export default async function date(text, config = {}) {
33
+ const { maxAttempts = 3, llm, ...options } = config;
34
+ const llmExpectations = (await toObject(
35
+ await chatGPT(expectationPrompt(text), { modelOptions: { ...llm }, ...options }),
36
+ null,
37
+ { llm, ...options }
38
+ )) || ['The result is a valid date'];
39
+
40
+ let attemptText = text;
41
+ let response;
42
+ for (let i = 0; i < maxAttempts; i += 1) {
43
+ const datePrompt = `${contentIsQuestion} ${attemptText}\n\n${explainAndSeparate} ${explainAndSeparatePrimitive}\n\n${asDate} ${asUndefinedByDefault}`;
44
+ // eslint-disable-next-line no-await-in-loop
45
+ response = await chatGPT(datePrompt, { modelOptions: { ...llm }, ...options });
46
+ const value = toDate(stripResponse(response));
47
+ if (value === undefined) return undefined;
48
+
49
+ // Convert to UTC date for consistent checks
50
+ const utcValue = new Date(
51
+ Date.UTC(value.getUTCFullYear(), value.getUTCMonth(), value.getUTCDate())
52
+ );
53
+
54
+ let failedCheck;
55
+ for (const check of llmExpectations) {
56
+ // eslint-disable-next-line no-await-in-loop
57
+ const passed = await bool(buildCheckPrompt(utcValue, check), { llm, ...options });
58
+ if (!passed) {
59
+ failedCheck = check;
60
+ break;
61
+ }
62
+ }
63
+
64
+ if (!failedCheck) return utcValue;
65
+
66
+ attemptText = `${text} The previous answer (${utcValue.toISOString()}) failed to satisfy: "${failedCheck}". Try again.`;
67
+ }
68
+ const finalValue = toDate(stripResponse(response));
69
+ return finalValue
70
+ ? new Date(
71
+ Date.UTC(finalValue.getUTCFullYear(), finalValue.getUTCMonth(), finalValue.getUTCDate())
72
+ )
73
+ : undefined;
74
+ }
@@ -0,0 +1,62 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import date from './index.js';
3
+ import bool from '../../verblets/bool/index.js';
4
+
5
+ vi.mock('../../lib/chatgpt/index.js', () => ({
6
+ default: vi.fn(),
7
+ }));
8
+
9
+ vi.mock('../../verblets/bool/index.js', () => ({
10
+ default: vi.fn(),
11
+ }));
12
+
13
+ vi.mock('../../verblets/to-object/index.js', () => ({
14
+ default: vi.fn(),
15
+ }));
16
+
17
+ const chatGPT = (await import('../../lib/chatgpt/index.js')).default;
18
+ const toObjectMock = (await import('../../verblets/to-object/index.js')).default;
19
+
20
+ describe('date chain', () => {
21
+ beforeEach(() => {
22
+ vi.clearAllMocks();
23
+ });
24
+ it('returns a date when bool approves', async () => {
25
+ chatGPT.mockResolvedValueOnce('["check"]');
26
+ toObjectMock.mockResolvedValueOnce(['check']);
27
+ chatGPT.mockResolvedValueOnce('2023-01-02');
28
+ bool.mockResolvedValueOnce(true);
29
+ const result = await date('When is tomorrow?');
30
+ expect(result instanceof Date).toBe(true);
31
+ expect(result.toISOString().startsWith('2023-01-02')).toBe(true);
32
+ expect(chatGPT).toHaveBeenCalledTimes(2);
33
+ expect(toObjectMock).toHaveBeenCalledTimes(1);
34
+ expect(bool).toHaveBeenCalledTimes(1);
35
+ });
36
+
37
+ it('retries until bool approves', async () => {
38
+ chatGPT.mockResolvedValueOnce('["check"]');
39
+ toObjectMock.mockResolvedValueOnce(['check']);
40
+ chatGPT.mockResolvedValueOnce('2023-01-02');
41
+ chatGPT.mockResolvedValueOnce('2023-02-03');
42
+ bool.mockResolvedValueOnce(false);
43
+ bool.mockResolvedValueOnce(true);
44
+
45
+ const result = await date('When is tomorrow?', { maxAttempts: 2 });
46
+ expect(result.toISOString().startsWith('2023-02-03')).toBe(true);
47
+ expect(chatGPT).toHaveBeenCalledTimes(3);
48
+ expect(toObjectMock).toHaveBeenCalledTimes(1);
49
+ expect(bool).toHaveBeenCalledTimes(2);
50
+ });
51
+
52
+ it('returns undefined when chatGPT says undefined', async () => {
53
+ chatGPT.mockResolvedValueOnce('["check"]');
54
+ toObjectMock.mockResolvedValueOnce(['check']);
55
+ chatGPT.mockResolvedValueOnce('undefined');
56
+ const result = await date('Unknown date');
57
+ expect(result).toBeUndefined();
58
+ expect(chatGPT).toHaveBeenCalledTimes(2);
59
+ expect(toObjectMock).toHaveBeenCalledTimes(1);
60
+ expect(bool).not.toHaveBeenCalled();
61
+ });
62
+ });
@@ -0,0 +1,22 @@
1
+ # disambiguate
2
+
3
+ Determine the intended meaning of a polysemous word or short phrase based on surrounding context.
4
+ The chain lists common meanings and filters them down to the one that fits best.
5
+
6
+ ```javascript
7
+ import disambiguate from './index.js';
8
+
9
+ const result = await disambiguate({
10
+ term: 'bat',
11
+ context: 'The child swung the bat at the baseball.'
12
+ });
13
+
14
+ console.log(result.meaning);
15
+ // => "a club used in sports like baseball"
16
+ ```
17
+
18
+ ## Use case: clarifying travel conversations
19
+
20
+ When a traveler says, "I spoke with the coach about my seat," it helps to know
21
+ whether they mean a sports instructor or an airline seating class. This chain
22
+ uses language model reasoning to resolve that ambiguity automatically.
@@ -0,0 +1,16 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "properties": {
5
+ "meanings": {
6
+ "type": "array",
7
+ "description": "Array of distinct dictionary meanings or common uses of a term",
8
+ "items": {
9
+ "type": "string",
10
+ "description": "A distinct meaning or common use of the term"
11
+ }
12
+ }
13
+ },
14
+ "required": ["meanings"],
15
+ "additionalProperties": false
16
+ }
@@ -0,0 +1,18 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import disambiguate from './index.js';
3
+ import { longTestTimeout } from '../../constants/common.js';
4
+
5
+ describe('Disambiguate chain', () => {
6
+ it(
7
+ 'contextual meaning: bank',
8
+ async () => {
9
+ const result = await disambiguate({
10
+ term: 'bank',
11
+ context: 'She waited in line at the bank to deposit her paycheck.',
12
+ });
13
+ expect(typeof result.meaning).toBe('string');
14
+ expect(result.meaning.length).toBeGreaterThan(0);
15
+ },
16
+ longTestTimeout
17
+ );
18
+ });