@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.
Files changed (330) hide show
  1. package/README.md +86 -213
  2. package/dist/index.browser.js +74 -0
  3. package/dist/index.js +548 -0
  4. package/dist/shared-C6kPWghF.js +7806 -0
  5. package/package.json +32 -11
  6. package/.cursor/launch.json +0 -30
  7. package/.cursor/settings.json +0 -20
  8. package/.github/workflows/branch-protection.yml +0 -22
  9. package/.github/workflows/ci.yml +0 -165
  10. package/.husky/pre-commit +0 -4
  11. package/.prettierrc +0 -6
  12. package/.release-it.json +0 -12
  13. package/.vitest.config.examples.js +0 -12
  14. package/.vitest.config.js +0 -8
  15. package/.vscode/launch.json +0 -31
  16. package/AGENTS.md +0 -220
  17. package/DEVELOPING.md +0 -105
  18. package/docker-compose.yml +0 -7
  19. package/eslint.config.js +0 -80
  20. package/scripts/clear-redis.js +0 -74
  21. package/scripts/generate-chain/index.js +0 -111
  22. package/scripts/generate-lib/index.js +0 -68
  23. package/scripts/generate-test/index.js +0 -137
  24. package/scripts/generate-verblet/README.md +0 -17
  25. package/scripts/generate-verblet/index.js +0 -110
  26. package/scripts/run.sh +0 -15
  27. package/scripts/runner/index.js +0 -56
  28. package/scripts/simple-editor/README.md +0 -34
  29. package/scripts/simple-editor/index.js +0 -79
  30. package/scripts/summarize-files/index.js +0 -70
  31. package/src/chains/README.md +0 -30
  32. package/src/chains/anonymize/README.md +0 -21
  33. package/src/chains/anonymize/index.examples.js +0 -75
  34. package/src/chains/anonymize/index.js +0 -121
  35. package/src/chains/anonymize/index.spec.js +0 -78
  36. package/src/chains/bulk-central-tendency/index.examples.js +0 -138
  37. package/src/chains/bulk-central-tendency/index.js +0 -91
  38. package/src/chains/bulk-filter/README.md +0 -21
  39. package/src/chains/bulk-filter/index.examples.js +0 -22
  40. package/src/chains/bulk-filter/index.js +0 -58
  41. package/src/chains/bulk-filter/index.spec.js +0 -38
  42. package/src/chains/bulk-find/README.md +0 -16
  43. package/src/chains/bulk-find/index.examples.js +0 -20
  44. package/src/chains/bulk-find/index.js +0 -30
  45. package/src/chains/bulk-find/index.spec.js +0 -26
  46. package/src/chains/bulk-group/README.md +0 -23
  47. package/src/chains/bulk-group/index.examples.js +0 -18
  48. package/src/chains/bulk-group/index.js +0 -34
  49. package/src/chains/bulk-group/index.spec.js +0 -41
  50. package/src/chains/bulk-map/README.md +0 -43
  51. package/src/chains/bulk-map/index.examples.js +0 -17
  52. package/src/chains/bulk-map/index.js +0 -86
  53. package/src/chains/bulk-map/index.spec.js +0 -44
  54. package/src/chains/bulk-reduce/README.md +0 -12
  55. package/src/chains/bulk-reduce/index.examples.js +0 -15
  56. package/src/chains/bulk-reduce/index.js +0 -13
  57. package/src/chains/bulk-reduce/index.spec.js +0 -25
  58. package/src/chains/bulk-score/README.md +0 -16
  59. package/src/chains/bulk-score/bulk-score-result.json +0 -18
  60. package/src/chains/bulk-score/index.examples.js +0 -22
  61. package/src/chains/bulk-score/index.js +0 -133
  62. package/src/chains/bulk-score/index.spec.js +0 -30
  63. package/src/chains/category-samples/README.md +0 -61
  64. package/src/chains/category-samples/index.examples.js +0 -103
  65. package/src/chains/category-samples/index.js +0 -134
  66. package/src/chains/collect-terms/README.md +0 -12
  67. package/src/chains/collect-terms/index.examples.js +0 -16
  68. package/src/chains/collect-terms/index.js +0 -44
  69. package/src/chains/collect-terms/index.spec.js +0 -25
  70. package/src/chains/conversation/README.md +0 -26
  71. package/src/chains/conversation/index.examples.js +0 -398
  72. package/src/chains/conversation/index.js +0 -126
  73. package/src/chains/conversation/index.spec.js +0 -148
  74. package/src/chains/conversation/turn-policies.js +0 -93
  75. package/src/chains/conversation/turn-policies.md +0 -123
  76. package/src/chains/conversation/turn-policies.spec.js +0 -135
  77. package/src/chains/date/README.md +0 -12
  78. package/src/chains/date/index.examples.js +0 -47
  79. package/src/chains/date/index.js +0 -74
  80. package/src/chains/date/index.spec.js +0 -62
  81. package/src/chains/disambiguate/README.md +0 -22
  82. package/src/chains/disambiguate/disambiguate-meanings-result.json +0 -16
  83. package/src/chains/disambiguate/index.examples.js +0 -18
  84. package/src/chains/disambiguate/index.js +0 -92
  85. package/src/chains/disambiguate/index.spec.js +0 -25
  86. package/src/chains/dismantle/README.md +0 -67
  87. package/src/chains/dismantle/dismantle.examples.js +0 -27
  88. package/src/chains/dismantle/index.examples.js +0 -30
  89. package/src/chains/dismantle/index.js +0 -303
  90. package/src/chains/dismantle/index.spec.js +0 -32
  91. package/src/chains/expect/README.md +0 -171
  92. package/src/chains/expect/index.examples.js +0 -146
  93. package/src/chains/expect/index.js +0 -207
  94. package/src/chains/expect/index.spec.js +0 -324
  95. package/src/chains/filter-ambiguous/README.md +0 -11
  96. package/src/chains/filter-ambiguous/index.examples.js +0 -20
  97. package/src/chains/filter-ambiguous/index.js +0 -49
  98. package/src/chains/filter-ambiguous/index.spec.js +0 -31
  99. package/src/chains/glossary/README.md +0 -19
  100. package/src/chains/glossary/index.examples.js +0 -386
  101. package/src/chains/glossary/index.js +0 -75
  102. package/src/chains/glossary/index.spec.js +0 -19
  103. package/src/chains/intersections/README.md +0 -166
  104. package/src/chains/intersections/index.examples.js +0 -280
  105. package/src/chains/intersections/index.js +0 -218
  106. package/src/chains/intersections/intersection-result.json +0 -38
  107. package/src/chains/list/index.examples.js +0 -68
  108. package/src/chains/list/index.js +0 -214
  109. package/src/chains/list/index.spec.js +0 -67
  110. package/src/chains/list/list-result.json +0 -16
  111. package/src/chains/list/schema.json +0 -24
  112. package/src/chains/llm-logger/README.md +0 -366
  113. package/src/chains/llm-logger/index.js +0 -591
  114. package/src/chains/llm-logger/index.spec.js +0 -391
  115. package/src/chains/llm-logger/schema.json +0 -105
  116. package/src/chains/questions/index.examples.js +0 -69
  117. package/src/chains/questions/index.js +0 -135
  118. package/src/chains/questions/index.spec.js +0 -29
  119. package/src/chains/scan-js/index.js +0 -116
  120. package/src/chains/set-interval/README.md +0 -81
  121. package/src/chains/set-interval/index.examples.js +0 -64
  122. package/src/chains/set-interval/index.js +0 -152
  123. package/src/chains/set-interval/index.spec.js +0 -70
  124. package/src/chains/socratic/README.md +0 -17
  125. package/src/chains/socratic/index.js +0 -64
  126. package/src/chains/socratic/index.spec.js +0 -24
  127. package/src/chains/sort/index.examples.js +0 -36
  128. package/src/chains/sort/index.js +0 -163
  129. package/src/chains/sort/index.spec.js +0 -112
  130. package/src/chains/sort/sort-result.json +0 -16
  131. package/src/chains/summary-map/README.md +0 -41
  132. package/src/chains/summary-map/index.examples.js +0 -64
  133. package/src/chains/summary-map/index.js +0 -226
  134. package/src/chains/summary-map/index.spec.js +0 -153
  135. package/src/chains/test/index.js +0 -114
  136. package/src/chains/test-advice/index.js +0 -35
  137. package/src/chains/themes/README.md +0 -20
  138. package/src/chains/themes/index.examples.js +0 -17
  139. package/src/chains/themes/index.js +0 -28
  140. package/src/chains/themes/index.spec.js +0 -19
  141. package/src/chains/veiled-variants/index.examples.js +0 -18
  142. package/src/chains/veiled-variants/index.js +0 -107
  143. package/src/chains/veiled-variants/index.spec.js +0 -40
  144. package/src/constants/common.js +0 -13
  145. package/src/constants/messages.js +0 -3
  146. package/src/constants/models.js +0 -184
  147. package/src/index.js +0 -203
  148. package/src/json-schemas/README.md +0 -13
  149. package/src/json-schemas/cars-test.json +0 -11
  150. package/src/json-schemas/index.js +0 -12
  151. package/src/json-schemas/intent.json +0 -38
  152. package/src/json-schemas/schema-dot-org-photograph.json +0 -133
  153. package/src/json-schemas/schema-dot-org-place.json +0 -129
  154. package/src/lib/README.md +0 -26
  155. package/src/lib/any-signal/index.js +0 -28
  156. package/src/lib/assert/README.md +0 -84
  157. package/src/lib/assert/index.js +0 -50
  158. package/src/lib/bulk-filter/README.md +0 -22
  159. package/src/lib/bulk-filter/index.examples.js +0 -27
  160. package/src/lib/bulk-filter/index.js +0 -63
  161. package/src/lib/bulk-filter/index.spec.js +0 -38
  162. package/src/lib/bulk-find/README.md +0 -18
  163. package/src/lib/bulk-find/index.examples.js +0 -19
  164. package/src/lib/bulk-find/index.js +0 -30
  165. package/src/lib/bulk-find/index.spec.js +0 -41
  166. package/src/lib/chatgpt/index.js +0 -163
  167. package/src/lib/combinations/index.js +0 -30
  168. package/src/lib/combinations/index.spec.js +0 -23
  169. package/src/lib/editor/index.js +0 -31
  170. package/src/lib/functional/index.js +0 -28
  171. package/src/lib/logger-service/index.js +0 -32
  172. package/src/lib/parse-js-parts/index.js +0 -321
  173. package/src/lib/parse-js-parts/index.spec.js +0 -156
  174. package/src/lib/parse-llm-list/README.md +0 -39
  175. package/src/lib/parse-llm-list/index.js +0 -54
  176. package/src/lib/parse-llm-list/index.spec.js +0 -59
  177. package/src/lib/path-aliases/index.js +0 -37
  178. package/src/lib/path-aliases/index.spec.js +0 -64
  179. package/src/lib/pave/index.js +0 -34
  180. package/src/lib/pave/index.spec.js +0 -76
  181. package/src/lib/prompt-cache/index.js +0 -50
  182. package/src/lib/retry/index.js +0 -66
  183. package/src/lib/retry/index.spec.js +0 -86
  184. package/src/lib/ring-buffer/README.md +0 -82
  185. package/src/lib/ring-buffer/index.js +0 -235
  186. package/src/lib/ring-buffer/index.spec.js +0 -388
  187. package/src/lib/search-best-first/city-walk.spec.js +0 -37
  188. package/src/lib/search-best-first/index.js +0 -97
  189. package/src/lib/search-best-first/index.spec.js +0 -35
  190. package/src/lib/search-js-files/code-features-property-definitions.json +0 -123
  191. package/src/lib/search-js-files/index.examples.js +0 -22
  192. package/src/lib/search-js-files/index.js +0 -155
  193. package/src/lib/search-js-files/index.spec.js +0 -34
  194. package/src/lib/search-js-files/scan-file.js +0 -242
  195. package/src/lib/shorten-text/index.js +0 -25
  196. package/src/lib/shorten-text/index.spec.js +0 -68
  197. package/src/lib/strip-numeric/index.js +0 -5
  198. package/src/lib/strip-response/index.js +0 -30
  199. package/src/lib/template-replace/index.js +0 -23
  200. package/src/lib/template-replace/index.spec.js +0 -60
  201. package/src/lib/timed-abort-controller/index.js +0 -41
  202. package/src/lib/to-bool/index.js +0 -8
  203. package/src/lib/to-date/index.js +0 -11
  204. package/src/lib/to-enum/index.js +0 -14
  205. package/src/lib/to-number/index.js +0 -12
  206. package/src/lib/to-number-with-units/index.js +0 -51
  207. package/src/lib/transcribe/index.js +0 -78
  208. package/src/prompts/README.md +0 -17
  209. package/src/prompts/as-enum.js +0 -5
  210. package/src/prompts/as-json-schema.js +0 -9
  211. package/src/prompts/as-object-with-schema.js +0 -26
  212. package/src/prompts/as-schema-org-text.js +0 -25
  213. package/src/prompts/as-schema-org-type.js +0 -1
  214. package/src/prompts/blog-post.js +0 -7
  215. package/src/prompts/code-features.js +0 -24
  216. package/src/prompts/constants.js +0 -101
  217. package/src/prompts/features-json-schema.js +0 -27
  218. package/src/prompts/generate-collection.js +0 -26
  219. package/src/prompts/generate-list.js +0 -48
  220. package/src/prompts/generate-questions.js +0 -19
  221. package/src/prompts/index.js +0 -20
  222. package/src/prompts/intent.js +0 -60
  223. package/src/prompts/output-succinct-names.js +0 -3
  224. package/src/prompts/select-from-threshold.js +0 -17
  225. package/src/prompts/sort.js +0 -31
  226. package/src/prompts/style.js +0 -38
  227. package/src/prompts/summarize.js +0 -13
  228. package/src/prompts/token-budget.js +0 -3
  229. package/src/prompts/wrap-list.js +0 -11
  230. package/src/prompts/wrap-variable.js +0 -36
  231. package/src/services/llm-model/global-overrides.spec.js +0 -432
  232. package/src/services/llm-model/index.js +0 -308
  233. package/src/services/llm-model/model.js +0 -21
  234. package/src/services/llm-model/negotiate.spec.js +0 -447
  235. package/src/services/redis/index.js +0 -147
  236. package/src/test/setup.js +0 -20
  237. package/src/verblets/README.md +0 -26
  238. package/src/verblets/auto/index.examples.js +0 -31
  239. package/src/verblets/auto/index.js +0 -28
  240. package/src/verblets/auto/index.spec.js +0 -32
  241. package/src/verblets/bool/README.md +0 -36
  242. package/src/verblets/bool/index.examples.js +0 -80
  243. package/src/verblets/bool/index.js +0 -25
  244. package/src/verblets/bool/index.schema.json +0 -14
  245. package/src/verblets/bool/index.spec.js +0 -33
  246. package/src/verblets/central-tendency/README.md +0 -166
  247. package/src/verblets/central-tendency/central-tendency-result.json +0 -24
  248. package/src/verblets/central-tendency/index.examples.js +0 -196
  249. package/src/verblets/central-tendency/index.js +0 -171
  250. package/src/verblets/central-tendency/index.spec.js +0 -148
  251. package/src/verblets/conversation-turn/README.md +0 -33
  252. package/src/verblets/conversation-turn/index.examples.js +0 -218
  253. package/src/verblets/conversation-turn/index.js +0 -68
  254. package/src/verblets/conversation-turn/index.spec.js +0 -77
  255. package/src/verblets/conversation-turn-multi/README.md +0 -31
  256. package/src/verblets/conversation-turn-multi/index.examples.js +0 -160
  257. package/src/verblets/conversation-turn-multi/index.js +0 -104
  258. package/src/verblets/conversation-turn-multi/index.spec.js +0 -63
  259. package/src/verblets/enum/index.examples.js +0 -30
  260. package/src/verblets/enum/index.js +0 -18
  261. package/src/verblets/enum/index.spec.js +0 -35
  262. package/src/verblets/expect/README.md +0 -64
  263. package/src/verblets/expect/index.examples.js +0 -109
  264. package/src/verblets/expect/index.js +0 -75
  265. package/src/verblets/expect/index.spec.js +0 -127
  266. package/src/verblets/intent/index.examples.js +0 -139
  267. package/src/verblets/intent/index.js +0 -60
  268. package/src/verblets/intent/index.spec.js +0 -31
  269. package/src/verblets/intersection/README.md +0 -16
  270. package/src/verblets/intersection/index.examples.js +0 -89
  271. package/src/verblets/intersection/index.js +0 -125
  272. package/src/verblets/intersection/index.spec.js +0 -60
  273. package/src/verblets/intersection/intersection-result.json +0 -16
  274. package/src/verblets/list-expand/README.md +0 -10
  275. package/src/verblets/list-expand/index.examples.js +0 -14
  276. package/src/verblets/list-expand/index.js +0 -104
  277. package/src/verblets/list-expand/index.spec.js +0 -18
  278. package/src/verblets/list-expand/list-expand-result.json +0 -16
  279. package/src/verblets/list-filter/README.md +0 -22
  280. package/src/verblets/list-filter/index.examples.js +0 -26
  281. package/src/verblets/list-filter/index.js +0 -18
  282. package/src/verblets/list-filter/index.spec.js +0 -19
  283. package/src/verblets/list-find/README.md +0 -11
  284. package/src/verblets/list-find/index.examples.js +0 -15
  285. package/src/verblets/list-find/index.js +0 -17
  286. package/src/verblets/list-find/index.spec.js +0 -19
  287. package/src/verblets/list-group/README.md +0 -16
  288. package/src/verblets/list-group/index.examples.js +0 -16
  289. package/src/verblets/list-group/index.js +0 -112
  290. package/src/verblets/list-group/index.spec.js +0 -35
  291. package/src/verblets/list-group/list-group-result.json +0 -16
  292. package/src/verblets/list-map/README.md +0 -11
  293. package/src/verblets/list-map/index.examples.js +0 -15
  294. package/src/verblets/list-map/index.js +0 -26
  295. package/src/verblets/list-map/index.spec.js +0 -17
  296. package/src/verblets/list-reduce/README.md +0 -10
  297. package/src/verblets/list-reduce/index.examples.js +0 -14
  298. package/src/verblets/list-reduce/index.js +0 -21
  299. package/src/verblets/list-reduce/index.spec.js +0 -27
  300. package/src/verblets/list-reduce/index.spec.jsx +0 -27
  301. package/src/verblets/name/README.md +0 -15
  302. package/src/verblets/name/index.examples.js +0 -28
  303. package/src/verblets/name/index.js +0 -19
  304. package/src/verblets/name/index.spec.js +0 -33
  305. package/src/verblets/name-similar-to/README.md +0 -26
  306. package/src/verblets/name-similar-to/index.examples.js +0 -18
  307. package/src/verblets/name-similar-to/index.js +0 -20
  308. package/src/verblets/name-similar-to/index.spec.js +0 -13
  309. package/src/verblets/number/index.examples.js +0 -199
  310. package/src/verblets/number/index.js +0 -25
  311. package/src/verblets/number/index.spec.js +0 -33
  312. package/src/verblets/number-with-units/index.examples.js +0 -38
  313. package/src/verblets/number-with-units/index.js +0 -84
  314. package/src/verblets/number-with-units/index.spec.js +0 -46
  315. package/src/verblets/number-with-units/number-with-units-result.json +0 -23
  316. package/src/verblets/people-list/README.md +0 -28
  317. package/src/verblets/people-list/index.examples.js +0 -184
  318. package/src/verblets/people-list/index.js +0 -44
  319. package/src/verblets/people-list/index.spec.js +0 -49
  320. package/src/verblets/schema-org/index.examples.js +0 -51
  321. package/src/verblets/schema-org/index.js +0 -37
  322. package/src/verblets/schema-org/index.spec.js +0 -39
  323. package/src/verblets/sentiment/README.md +0 -10
  324. package/src/verblets/sentiment/index.examples.js +0 -20
  325. package/src/verblets/sentiment/index.js +0 -9
  326. package/src/verblets/sentiment/index.spec.js +0 -20
  327. package/src/verblets/to-object/README.md +0 -38
  328. package/src/verblets/to-object/index.examples.js +0 -29
  329. package/src/verblets/to-object/index.js +0 -131
  330. 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
- });