@far-world-labs/verblets 0.1.7 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (306) 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 -8
  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 -8
  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/generate-chain/index.js +0 -111
  21. package/scripts/generate-lib/index.js +0 -68
  22. package/scripts/generate-test/index.js +0 -137
  23. package/scripts/generate-verblet/README.md +0 -17
  24. package/scripts/generate-verblet/index.js +0 -110
  25. package/scripts/run.sh +0 -15
  26. package/scripts/runner/index.js +0 -56
  27. package/scripts/simple-editor/README.md +0 -34
  28. package/scripts/simple-editor/index.js +0 -79
  29. package/scripts/summarize-files/index.js +0 -70
  30. package/src/chains/README.md +0 -30
  31. package/src/chains/anonymize/README.md +0 -21
  32. package/src/chains/anonymize/index.examples.js +0 -75
  33. package/src/chains/anonymize/index.js +0 -121
  34. package/src/chains/anonymize/index.spec.js +0 -78
  35. package/src/chains/bulk-central-tendency/index.examples.js +0 -138
  36. package/src/chains/bulk-central-tendency/index.js +0 -91
  37. package/src/chains/bulk-filter/README.md +0 -21
  38. package/src/chains/bulk-filter/index.examples.js +0 -22
  39. package/src/chains/bulk-filter/index.js +0 -58
  40. package/src/chains/bulk-filter/index.spec.js +0 -38
  41. package/src/chains/bulk-find/README.md +0 -16
  42. package/src/chains/bulk-find/index.examples.js +0 -20
  43. package/src/chains/bulk-find/index.js +0 -30
  44. package/src/chains/bulk-find/index.spec.js +0 -26
  45. package/src/chains/bulk-group/README.md +0 -23
  46. package/src/chains/bulk-group/index.examples.js +0 -18
  47. package/src/chains/bulk-group/index.js +0 -34
  48. package/src/chains/bulk-group/index.spec.js +0 -41
  49. package/src/chains/bulk-map/README.md +0 -43
  50. package/src/chains/bulk-map/index.examples.js +0 -17
  51. package/src/chains/bulk-map/index.js +0 -86
  52. package/src/chains/bulk-map/index.spec.js +0 -44
  53. package/src/chains/bulk-reduce/README.md +0 -12
  54. package/src/chains/bulk-reduce/index.examples.js +0 -15
  55. package/src/chains/bulk-reduce/index.js +0 -13
  56. package/src/chains/bulk-reduce/index.spec.js +0 -25
  57. package/src/chains/bulk-score/README.md +0 -16
  58. package/src/chains/bulk-score/bulk-score-result.json +0 -18
  59. package/src/chains/bulk-score/index.examples.js +0 -22
  60. package/src/chains/bulk-score/index.js +0 -133
  61. package/src/chains/bulk-score/index.spec.js +0 -30
  62. package/src/chains/category-samples/README.md +0 -61
  63. package/src/chains/category-samples/index.examples.js +0 -103
  64. package/src/chains/category-samples/index.js +0 -134
  65. package/src/chains/collect-terms/README.md +0 -12
  66. package/src/chains/collect-terms/index.examples.js +0 -16
  67. package/src/chains/collect-terms/index.js +0 -44
  68. package/src/chains/collect-terms/index.spec.js +0 -25
  69. package/src/chains/date/README.md +0 -12
  70. package/src/chains/date/index.examples.js +0 -47
  71. package/src/chains/date/index.js +0 -74
  72. package/src/chains/date/index.spec.js +0 -62
  73. package/src/chains/disambiguate/README.md +0 -22
  74. package/src/chains/disambiguate/disambiguate-meanings-result.json +0 -16
  75. package/src/chains/disambiguate/index.examples.js +0 -18
  76. package/src/chains/disambiguate/index.js +0 -92
  77. package/src/chains/disambiguate/index.spec.js +0 -25
  78. package/src/chains/dismantle/README.md +0 -67
  79. package/src/chains/dismantle/dismantle.examples.js +0 -27
  80. package/src/chains/dismantle/index.examples.js +0 -30
  81. package/src/chains/dismantle/index.js +0 -303
  82. package/src/chains/dismantle/index.spec.js +0 -32
  83. package/src/chains/expect/README.md +0 -171
  84. package/src/chains/expect/index.examples.js +0 -146
  85. package/src/chains/expect/index.js +0 -173
  86. package/src/chains/expect/index.spec.js +0 -324
  87. package/src/chains/filter-ambiguous/README.md +0 -11
  88. package/src/chains/filter-ambiguous/index.examples.js +0 -20
  89. package/src/chains/filter-ambiguous/index.js +0 -49
  90. package/src/chains/filter-ambiguous/index.spec.js +0 -31
  91. package/src/chains/glossary/README.md +0 -19
  92. package/src/chains/glossary/index.examples.js +0 -386
  93. package/src/chains/glossary/index.js +0 -75
  94. package/src/chains/glossary/index.spec.js +0 -19
  95. package/src/chains/intersections/README.md +0 -152
  96. package/src/chains/intersections/index.examples.js +0 -279
  97. package/src/chains/intersections/index.js +0 -366
  98. package/src/chains/intersections/intersection-result.json +0 -38
  99. package/src/chains/list/index.examples.js +0 -68
  100. package/src/chains/list/index.js +0 -214
  101. package/src/chains/list/index.spec.js +0 -67
  102. package/src/chains/list/list-result.json +0 -16
  103. package/src/chains/list/schema.json +0 -24
  104. package/src/chains/llm-logger/README.md +0 -208
  105. package/src/chains/llm-logger/index.js +0 -205
  106. package/src/chains/llm-logger/index.spec.js +0 -330
  107. package/src/chains/questions/index.examples.js +0 -69
  108. package/src/chains/questions/index.js +0 -135
  109. package/src/chains/questions/index.spec.js +0 -29
  110. package/src/chains/scan-js/index.js +0 -116
  111. package/src/chains/set-interval/README.md +0 -81
  112. package/src/chains/set-interval/index.examples.js +0 -36
  113. package/src/chains/set-interval/index.js +0 -131
  114. package/src/chains/set-interval/index.spec.js +0 -70
  115. package/src/chains/socratic/README.md +0 -17
  116. package/src/chains/socratic/index.js +0 -64
  117. package/src/chains/socratic/index.spec.js +0 -24
  118. package/src/chains/sort/index.examples.js +0 -36
  119. package/src/chains/sort/index.js +0 -163
  120. package/src/chains/sort/index.spec.js +0 -112
  121. package/src/chains/sort/sort-result.json +0 -16
  122. package/src/chains/summary-map/README.md +0 -41
  123. package/src/chains/summary-map/index.examples.js +0 -64
  124. package/src/chains/summary-map/index.js +0 -226
  125. package/src/chains/summary-map/index.spec.js +0 -153
  126. package/src/chains/test/index.js +0 -114
  127. package/src/chains/test-advice/index.js +0 -35
  128. package/src/chains/themes/README.md +0 -20
  129. package/src/chains/themes/index.examples.js +0 -17
  130. package/src/chains/themes/index.js +0 -28
  131. package/src/chains/themes/index.spec.js +0 -19
  132. package/src/chains/veiled-variants/index.examples.js +0 -18
  133. package/src/chains/veiled-variants/index.js +0 -107
  134. package/src/chains/veiled-variants/index.spec.js +0 -40
  135. package/src/constants/common.js +0 -7
  136. package/src/constants/messages.js +0 -3
  137. package/src/constants/models.js +0 -183
  138. package/src/index.js +0 -193
  139. package/src/json-schemas/README.md +0 -13
  140. package/src/json-schemas/cars-test.json +0 -11
  141. package/src/json-schemas/index.js +0 -12
  142. package/src/json-schemas/intent.json +0 -38
  143. package/src/json-schemas/schema-dot-org-photograph.json +0 -133
  144. package/src/json-schemas/schema-dot-org-place.json +0 -129
  145. package/src/lib/README.md +0 -26
  146. package/src/lib/any-signal/index.js +0 -28
  147. package/src/lib/bulk-filter/README.md +0 -22
  148. package/src/lib/bulk-filter/index.examples.js +0 -27
  149. package/src/lib/bulk-filter/index.js +0 -63
  150. package/src/lib/bulk-filter/index.spec.js +0 -38
  151. package/src/lib/bulk-find/README.md +0 -18
  152. package/src/lib/bulk-find/index.examples.js +0 -19
  153. package/src/lib/bulk-find/index.js +0 -30
  154. package/src/lib/bulk-find/index.spec.js +0 -41
  155. package/src/lib/chatgpt/index.js +0 -163
  156. package/src/lib/combinations/index.js +0 -30
  157. package/src/lib/combinations/index.spec.js +0 -23
  158. package/src/lib/editor/index.js +0 -31
  159. package/src/lib/functional/index.js +0 -28
  160. package/src/lib/logger-service/index.js +0 -32
  161. package/src/lib/parse-js-parts/index.js +0 -321
  162. package/src/lib/parse-js-parts/index.spec.js +0 -156
  163. package/src/lib/parse-llm-list/README.md +0 -39
  164. package/src/lib/parse-llm-list/index.js +0 -54
  165. package/src/lib/parse-llm-list/index.spec.js +0 -59
  166. package/src/lib/path-aliases/index.js +0 -37
  167. package/src/lib/path-aliases/index.spec.js +0 -64
  168. package/src/lib/pave/index.js +0 -34
  169. package/src/lib/pave/index.spec.js +0 -76
  170. package/src/lib/prompt-cache/index.js +0 -50
  171. package/src/lib/retry/index.js +0 -66
  172. package/src/lib/retry/index.spec.js +0 -86
  173. package/src/lib/ring-buffer/README.md +0 -460
  174. package/src/lib/ring-buffer/index.js +0 -1074
  175. package/src/lib/search-best-first/city-walk.spec.js +0 -37
  176. package/src/lib/search-best-first/index.js +0 -97
  177. package/src/lib/search-best-first/index.spec.js +0 -35
  178. package/src/lib/search-js-files/code-features-property-definitions.json +0 -123
  179. package/src/lib/search-js-files/index.examples.js +0 -22
  180. package/src/lib/search-js-files/index.js +0 -155
  181. package/src/lib/search-js-files/index.spec.js +0 -34
  182. package/src/lib/search-js-files/scan-file.js +0 -242
  183. package/src/lib/shorten-text/index.js +0 -25
  184. package/src/lib/shorten-text/index.spec.js +0 -68
  185. package/src/lib/strip-numeric/index.js +0 -5
  186. package/src/lib/strip-response/index.js +0 -30
  187. package/src/lib/template-replace/index.js +0 -23
  188. package/src/lib/template-replace/index.spec.js +0 -60
  189. package/src/lib/timed-abort-controller/index.js +0 -41
  190. package/src/lib/to-bool/index.js +0 -8
  191. package/src/lib/to-date/index.js +0 -11
  192. package/src/lib/to-enum/index.js +0 -14
  193. package/src/lib/to-number/index.js +0 -12
  194. package/src/lib/to-number-with-units/index.js +0 -51
  195. package/src/lib/transcribe/index.js +0 -78
  196. package/src/prompts/README.md +0 -17
  197. package/src/prompts/as-enum.js +0 -5
  198. package/src/prompts/as-json-schema.js +0 -9
  199. package/src/prompts/as-object-with-schema.js +0 -26
  200. package/src/prompts/as-schema-org-text.js +0 -25
  201. package/src/prompts/as-schema-org-type.js +0 -1
  202. package/src/prompts/blog-post.js +0 -7
  203. package/src/prompts/code-features.js +0 -24
  204. package/src/prompts/constants.js +0 -101
  205. package/src/prompts/features-json-schema.js +0 -27
  206. package/src/prompts/generate-collection.js +0 -26
  207. package/src/prompts/generate-list.js +0 -48
  208. package/src/prompts/generate-questions.js +0 -19
  209. package/src/prompts/index.js +0 -20
  210. package/src/prompts/intent.js +0 -60
  211. package/src/prompts/output-succinct-names.js +0 -3
  212. package/src/prompts/select-from-threshold.js +0 -17
  213. package/src/prompts/sort.js +0 -31
  214. package/src/prompts/style.js +0 -38
  215. package/src/prompts/summarize.js +0 -13
  216. package/src/prompts/token-budget.js +0 -3
  217. package/src/prompts/wrap-list.js +0 -11
  218. package/src/prompts/wrap-variable.js +0 -36
  219. package/src/services/llm-model/global-overrides.spec.js +0 -432
  220. package/src/services/llm-model/index.js +0 -308
  221. package/src/services/llm-model/model.js +0 -21
  222. package/src/services/llm-model/negotiate.spec.js +0 -447
  223. package/src/services/redis/index.js +0 -147
  224. package/src/test/setup.js +0 -20
  225. package/src/verblets/README.md +0 -26
  226. package/src/verblets/auto/index.examples.js +0 -31
  227. package/src/verblets/auto/index.js +0 -28
  228. package/src/verblets/auto/index.spec.js +0 -32
  229. package/src/verblets/bool/README.md +0 -36
  230. package/src/verblets/bool/index.examples.js +0 -80
  231. package/src/verblets/bool/index.js +0 -25
  232. package/src/verblets/bool/index.schema.json +0 -14
  233. package/src/verblets/bool/index.spec.js +0 -33
  234. package/src/verblets/central-tendency/README.md +0 -166
  235. package/src/verblets/central-tendency/central-tendency-result.json +0 -24
  236. package/src/verblets/central-tendency/index.examples.js +0 -196
  237. package/src/verblets/central-tendency/index.js +0 -171
  238. package/src/verblets/central-tendency/index.spec.js +0 -148
  239. package/src/verblets/enum/index.examples.js +0 -30
  240. package/src/verblets/enum/index.js +0 -18
  241. package/src/verblets/enum/index.spec.js +0 -35
  242. package/src/verblets/expect/README.md +0 -64
  243. package/src/verblets/expect/index.examples.js +0 -109
  244. package/src/verblets/expect/index.js +0 -75
  245. package/src/verblets/expect/index.spec.js +0 -127
  246. package/src/verblets/intent/index.examples.js +0 -139
  247. package/src/verblets/intent/index.js +0 -60
  248. package/src/verblets/intent/index.spec.js +0 -31
  249. package/src/verblets/intersection/README.md +0 -16
  250. package/src/verblets/intersection/index.examples.js +0 -89
  251. package/src/verblets/intersection/index.js +0 -84
  252. package/src/verblets/intersection/index.spec.js +0 -60
  253. package/src/verblets/intersection/intersection-result.json +0 -16
  254. package/src/verblets/list-expand/README.md +0 -10
  255. package/src/verblets/list-expand/index.examples.js +0 -14
  256. package/src/verblets/list-expand/index.js +0 -104
  257. package/src/verblets/list-expand/index.spec.js +0 -18
  258. package/src/verblets/list-expand/list-expand-result.json +0 -16
  259. package/src/verblets/list-filter/README.md +0 -22
  260. package/src/verblets/list-filter/index.examples.js +0 -26
  261. package/src/verblets/list-filter/index.js +0 -18
  262. package/src/verblets/list-filter/index.spec.js +0 -19
  263. package/src/verblets/list-find/README.md +0 -11
  264. package/src/verblets/list-find/index.examples.js +0 -15
  265. package/src/verblets/list-find/index.js +0 -17
  266. package/src/verblets/list-find/index.spec.js +0 -19
  267. package/src/verblets/list-group/README.md +0 -16
  268. package/src/verblets/list-group/index.examples.js +0 -16
  269. package/src/verblets/list-group/index.js +0 -112
  270. package/src/verblets/list-group/index.spec.js +0 -35
  271. package/src/verblets/list-group/list-group-result.json +0 -16
  272. package/src/verblets/list-map/README.md +0 -11
  273. package/src/verblets/list-map/index.examples.js +0 -15
  274. package/src/verblets/list-map/index.js +0 -26
  275. package/src/verblets/list-map/index.spec.js +0 -17
  276. package/src/verblets/list-reduce/README.md +0 -10
  277. package/src/verblets/list-reduce/index.examples.js +0 -14
  278. package/src/verblets/list-reduce/index.js +0 -21
  279. package/src/verblets/list-reduce/index.spec.js +0 -27
  280. package/src/verblets/list-reduce/index.spec.jsx +0 -27
  281. package/src/verblets/name/README.md +0 -15
  282. package/src/verblets/name/index.examples.js +0 -28
  283. package/src/verblets/name/index.js +0 -19
  284. package/src/verblets/name/index.spec.js +0 -33
  285. package/src/verblets/name-similar-to/README.md +0 -26
  286. package/src/verblets/name-similar-to/index.examples.js +0 -18
  287. package/src/verblets/name-similar-to/index.js +0 -20
  288. package/src/verblets/name-similar-to/index.spec.js +0 -13
  289. package/src/verblets/number/index.examples.js +0 -199
  290. package/src/verblets/number/index.js +0 -25
  291. package/src/verblets/number/index.spec.js +0 -33
  292. package/src/verblets/number-with-units/index.examples.js +0 -38
  293. package/src/verblets/number-with-units/index.js +0 -84
  294. package/src/verblets/number-with-units/index.spec.js +0 -46
  295. package/src/verblets/number-with-units/number-with-units-result.json +0 -23
  296. package/src/verblets/schema-org/index.examples.js +0 -51
  297. package/src/verblets/schema-org/index.js +0 -37
  298. package/src/verblets/schema-org/index.spec.js +0 -39
  299. package/src/verblets/sentiment/README.md +0 -10
  300. package/src/verblets/sentiment/index.examples.js +0 -20
  301. package/src/verblets/sentiment/index.js +0 -9
  302. package/src/verblets/sentiment/index.spec.js +0 -20
  303. package/src/verblets/to-object/README.md +0 -38
  304. package/src/verblets/to-object/index.examples.js +0 -29
  305. package/src/verblets/to-object/index.js +0 -131
  306. package/src/verblets/to-object/index.spec.js +0 -71
@@ -1,104 +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
-
7
- // Get the directory of this module
8
- const __filename = fileURLToPath(import.meta.url);
9
- const __dirname = path.dirname(__filename);
10
-
11
- /**
12
- * Load the JSON schema for list expand results
13
- * @returns {Promise<Object>} JSON schema for validation
14
- */
15
- async function getListExpandSchema() {
16
- const schemaPath = path.join(__dirname, 'list-expand-result.json');
17
- return JSON.parse(await fs.readFile(schemaPath, 'utf8'));
18
- }
19
-
20
- /**
21
- * Create model options for structured outputs
22
- * @param {string|Object} llm - LLM model name or configuration object
23
- * @returns {Promise<Object>} Model options for chatGPT
24
- */
25
- async function createModelOptions(llm = 'fastGoodCheap') {
26
- const schema = await getListExpandSchema();
27
-
28
- const responseFormat = {
29
- type: 'json_schema',
30
- json_schema: {
31
- name: 'list_expand_result',
32
- schema,
33
- },
34
- };
35
-
36
- if (typeof llm === 'string') {
37
- return {
38
- modelName: llm,
39
- response_format: responseFormat,
40
- };
41
- } else {
42
- return {
43
- ...llm,
44
- response_format: responseFormat,
45
- };
46
- }
47
- }
48
-
49
- // TODO: This could potentially be refactored to use the list chain (../../chains/list/index.js)
50
- // for better consistency, but would require adapting the list chain to support this simpler
51
- // expansion use case without changing the current behavior and test expectations.
52
-
53
- const buildPrompt = function (list, count) {
54
- const listBlock = wrapVariable(list.join('\n'), { tag: 'list' });
55
- return (
56
- `Expand <list> with new items that belong to the same category and ` +
57
- `match the style of the existing entries. Avoid duplicates or extraneous ` +
58
- `text. Continue adding entries until there are at least ${count} in total. ` +
59
- `Return a JSON object with an "items" array containing all the expanded items.\n\n${listBlock}`
60
- );
61
- };
62
-
63
- /**
64
- * Expand a list with new items that belong to the same category and match the style.
65
- * This is a simplified interface to the list chain for expansion use cases.
66
- *
67
- * @param {string[]} existingList - The list to expand
68
- * @param {number} targetCount - Target total count (default: double the input)
69
- * @param {Object} config - Configuration options including llm settings
70
- * @returns {Promise<string[]>} Expanded list
71
- */
72
- export default async function listExpand(list, count = list.length * 2, config = {}) {
73
- const { llm, ...options } = config;
74
- const modelOptions = await createModelOptions(llm);
75
- const output = await chatGPT(buildPrompt(list, count), { modelOptions, ...options });
76
-
77
- // With structured outputs, response should already be parsed and validated
78
- let parsed;
79
- if (typeof output === 'string') {
80
- try {
81
- parsed = JSON.parse(output);
82
- } catch {
83
- // Handle non-JSON responses (e.g., from mocks or fallback cases)
84
- // Split by newlines and filter out empty lines
85
- const lines = output
86
- .split('\n')
87
- .map((line) => line.trim())
88
- .filter((line) => line.length > 0);
89
- parsed = { items: lines };
90
- }
91
- } else {
92
- parsed = output;
93
- }
94
-
95
- // Extract items from the object structure
96
- const items = parsed?.items || parsed;
97
-
98
- if (!Array.isArray(items)) {
99
- console.warn('Expected items array, got:', typeof items);
100
- return [];
101
- }
102
-
103
- return items;
104
- }
@@ -1,18 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest';
2
- import listExpand from './index.js';
3
-
4
- vi.mock('../../lib/chatgpt/index.js', () => ({
5
- default: vi.fn(async (prompt) => {
6
- const match = prompt.match(/<list>\n([\s\S]*?)\n<\/list>/);
7
- const lines = match ? match[1].split('\n') : [];
8
- const extras = lines.map((l) => `${l}-extra`);
9
- return [...lines, ...extras].join('\n');
10
- }),
11
- }));
12
-
13
- describe('list-expand verblet', () => {
14
- it('expands items to at least the requested count', async () => {
15
- const result = await listExpand(['alpha', 'beta'], 4);
16
- expect(result).toStrictEqual(['alpha', 'beta', 'alpha-extra', 'beta-extra']);
17
- });
18
- });
@@ -1,16 +0,0 @@
1
- {
2
- "$schema": "http://json-schema.org/draft-07/schema#",
3
- "type": "object",
4
- "properties": {
5
- "items": {
6
- "type": "array",
7
- "description": "Array of expanded list items",
8
- "items": {
9
- "type": "string",
10
- "description": "A list item that belongs to the same category and matches the style"
11
- }
12
- }
13
- },
14
- "required": ["items"],
15
- "additionalProperties": false
16
- }
@@ -1,22 +0,0 @@
1
- # list-filter
2
-
3
- Filter a list with a single ChatGPT call using custom instructions.
4
-
5
- ```javascript
6
- import listFilter from './index.js';
7
-
8
- const reflections = [
9
- 'Losing that match taught me the value of persistence.',
10
- "I hate losing and it proves I'm worthless.",
11
- 'After failing my exam, I studied harder and passed the retake.',
12
- "No matter what I do, I'll never succeed.",
13
- ];
14
- const growth = await listFilter(
15
- reflections,
16
- 'keep only reflections that show personal growth or learning from mistakes'
17
- );
18
- // => [
19
- // 'Losing that match taught me the value of persistence.',
20
- // 'After failing my exam, I studied harder and passed the retake.',
21
- // ]
22
- ```
@@ -1,26 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import listFilter from './index.js';
3
- import { longTestTimeout } from '../../constants/common.js';
4
-
5
- describe('list-filter examples', () => {
6
- it(
7
- 'filters items with custom instructions',
8
- async () => {
9
- const entries = [
10
- 'Losing that match taught me the value of persistence.',
11
- "I hate losing and it proves I'm worthless.",
12
- 'After failing my exam, I studied harder and passed the retake.',
13
- "No matter what I do, I'll never succeed.",
14
- ];
15
- const result = await listFilter(
16
- entries,
17
- 'keep only reflections that show personal growth or learning from mistakes'
18
- );
19
- expect(result).toStrictEqual([
20
- 'Losing that match taught me the value of persistence.',
21
- 'After failing my exam, I studied harder and passed the retake.',
22
- ]);
23
- },
24
- longTestTimeout
25
- );
26
- });
@@ -1,18 +0,0 @@
1
- import chatGPT from '../../lib/chatgpt/index.js';
2
- import wrapVariable from '../../prompts/wrap-variable.js';
3
-
4
- function buildPrompt(list, instructions) {
5
- const instructionsBlock = wrapVariable(instructions, { tag: 'instructions' });
6
- const listBlock = wrapVariable(list.join('\n'), { tag: 'list' });
7
- return `From the <list>, select only the items that satisfy the <instructions>. Return one item per line without numbering. If none match, return an empty string.\n\n${instructionsBlock}\n${listBlock}`;
8
- }
9
-
10
- export default async function listFilter(list, instructions, config = {}) {
11
- const { llm, ...options } = config;
12
- const output = await chatGPT(buildPrompt(list, instructions), {
13
- modelOptions: { ...llm },
14
- ...options,
15
- });
16
- const trimmed = output.trim();
17
- return trimmed ? trimmed.split('\n') : [];
18
- }
@@ -1,19 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest';
2
- import listFilter from './index.js';
3
-
4
- vi.mock('../../lib/chatgpt/index.js', () => ({
5
- default: vi.fn(async (prompt) => {
6
- const listMatch = prompt.match(/<list>\n([\s\S]*?)\n<\/list>/);
7
- const lines = listMatch ? listMatch[1].split('\n') : [];
8
- const instMatch = prompt.match(/"([^"]+)"/);
9
- const letter = instMatch ? instMatch[1] : '';
10
- return lines.filter((l) => l.includes(letter)).join('\n');
11
- }),
12
- }));
13
-
14
- describe('list-filter verblet', () => {
15
- it('filters items using instructions', async () => {
16
- const result = await listFilter(['alpha', 'beta', 'gamma'], 'm');
17
- expect(result).toStrictEqual(['gamma']);
18
- });
19
- });
@@ -1,11 +0,0 @@
1
- # list-find
2
-
3
- Find the single best match in a list using natural language instructions.
4
-
5
- ```javascript
6
- import listFind from './index.js';
7
-
8
- const snacks = ['apple pie', 'fruit roll-up', 'carrot sticks'];
9
- await listFind(snacks, 'which snack feels most nostalgic for kids of the 90s?');
10
- // => 'fruit roll-up'
11
- ```
@@ -1,15 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import listFind from './index.js';
3
- import { longTestTimeout } from '../../constants/common.js';
4
-
5
- describe('list-find examples', () => {
6
- it(
7
- 'finds the item that best fits the instructions',
8
- async () => {
9
- const books = ['space adventure', 'medieval romance', 'futuristic mystery'];
10
- const result = await listFind(books, 'which book sounds most futuristic?');
11
- expect(result).toBeDefined();
12
- },
13
- longTestTimeout
14
- );
15
- });
@@ -1,17 +0,0 @@
1
- import chatGPT from '../../lib/chatgpt/index.js';
2
- import wrapVariable from '../../prompts/wrap-variable.js';
3
-
4
- const buildPrompt = (list, instructions) => {
5
- const instructionsBlock = wrapVariable(instructions, { tag: 'instructions' });
6
- const listBlock = wrapVariable(list.join('\n'), { tag: 'list' });
7
- return `From the <list>, select the single item that best satisfies the <instructions>. If none apply, return an empty string.\n\n${instructionsBlock}\n${listBlock}`;
8
- };
9
-
10
- export default async function listFind(list, instructions, config = {}) {
11
- const { llm, ...options } = config;
12
- const output = await chatGPT(buildPrompt(list, instructions), {
13
- modelOptions: { ...llm },
14
- ...options,
15
- });
16
- return output.trim();
17
- }
@@ -1,19 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest';
2
- import listFind from './index.js';
3
-
4
- vi.mock('../../lib/chatgpt/index.js', () => ({
5
- default: vi.fn(async (prompt) => {
6
- const listMatch = prompt.match(/<list>\n([\s\S]*?)\n<\/list>/);
7
- const lines = listMatch ? listMatch[1].split('\n') : [];
8
- const parts = prompt.split('"');
9
- const letter = [...parts].reverse().find((p) => /^[a-z]+$/i.test(p.trim())) || '';
10
- return lines.find((l) => l.includes(letter)) || '';
11
- }),
12
- }));
13
-
14
- describe('list-find verblet', () => {
15
- it('finds item using instructions', async () => {
16
- const result = await listFind(['alpha', 'beta', 'gamma'], 'find "b"');
17
- expect(result).toBe('beta');
18
- });
19
- });
@@ -1,16 +0,0 @@
1
- # list-group
2
-
3
- Group a list into stable groups using custom instructions. Optionally
4
- provide a list of categories to maintain consistency across runs.
5
-
6
- ```javascript
7
- import listGroup from './index.js';
8
-
9
- const categories = ['fruit', 'vegetable'];
10
- await listGroup(
11
- ['apple', 'banana', 'carrot'],
12
- 'Classify each item',
13
- categories
14
- );
15
- // => { fruit: ['apple', 'banana'], vegetable: ['carrot'] }
16
- ```
@@ -1,16 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import listGroup from './index.js';
3
- import { longTestTimeout } from '../../constants/common.js';
4
-
5
- describe('list-group examples', () => {
6
- it(
7
- 'groups a list into categories',
8
- async () => {
9
- const items = ['apple', 'beer', 'orange', 'wine', 'banana'];
10
- const result = await listGroup(items, 'Group as drinks or food', ['drink', 'food']);
11
- expect(typeof result).toBe('object');
12
- expect(Object.keys(result).length).toBeGreaterThan(0);
13
- },
14
- longTestTimeout
15
- );
16
- });
@@ -1,112 +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
-
7
- // Get the directory of this module
8
- const __filename = fileURLToPath(import.meta.url);
9
- const __dirname = path.dirname(__filename);
10
-
11
- /**
12
- * Load the JSON schema for list group results
13
- * @returns {Promise<Object>} JSON schema for validation
14
- */
15
- async function getListGroupSchema() {
16
- const schemaPath = path.join(__dirname, 'list-group-result.json');
17
- return JSON.parse(await fs.readFile(schemaPath, 'utf8'));
18
- }
19
-
20
- /**
21
- * Create model options for structured outputs
22
- * @param {string|Object} llm - LLM model name or configuration object
23
- * @returns {Promise<Object>} Model options for chatGPT
24
- */
25
- async function createModelOptions(llm = 'fastGoodCheap') {
26
- const schema = await getListGroupSchema();
27
-
28
- const responseFormat = {
29
- type: 'json_schema',
30
- json_schema: {
31
- name: 'list_group_result',
32
- schema,
33
- },
34
- };
35
-
36
- if (typeof llm === 'string') {
37
- return {
38
- modelName: llm,
39
- response_format: responseFormat,
40
- };
41
- } else {
42
- return {
43
- ...llm,
44
- response_format: responseFormat,
45
- };
46
- }
47
- }
48
-
49
- const buildPrompt = (list, instructions, categories) => {
50
- const instructionsBlock = wrapVariable(instructions, { tag: 'instructions' });
51
- const listBlock = wrapVariable(list.join('\n'), { tag: 'list' });
52
- const categoryBlock =
53
- categories && categories.length
54
- ? `${wrapVariable(categories.join('\n'), { tag: 'categories' })}\n`
55
- : '';
56
- const categoryText = categories && categories.length ? 'one of the <categories>' : 'a group';
57
-
58
- return `Assign each line in <list> to ${categoryText} according to <instructions>.
59
-
60
- Return a JSON object with a "labels" array containing exactly ${list.length} group names, one for each item in the same order as the input list.
61
-
62
- ${instructionsBlock}
63
- ${categoryBlock}${listBlock}
64
-
65
- Output format: {"labels": [array with exactly ${list.length} group names]}`;
66
- };
67
-
68
- export default async function listGroup(list, instructions, categories, config = {}) {
69
- const { llm, ...options } = config;
70
- const modelOptions = await createModelOptions(llm);
71
- const output = await chatGPT(buildPrompt(list, instructions, categories), {
72
- modelOptions,
73
- ...options,
74
- });
75
-
76
- // With structured outputs, response should already be parsed and validated
77
- let parsed;
78
- if (typeof output === 'string') {
79
- try {
80
- parsed = JSON.parse(output);
81
- } catch {
82
- // Handle non-JSON responses (e.g., from mocks or fallback cases)
83
- // Split by newlines and filter out empty lines
84
- const lines = output
85
- .split('\n')
86
- .map((line) => line.trim())
87
- .filter((line) => line.length > 0);
88
- parsed = { labels: lines };
89
- }
90
- } else {
91
- parsed = output;
92
- }
93
-
94
- // Extract labels from the object structure
95
- const labels = parsed?.labels || parsed;
96
-
97
- if (!Array.isArray(labels) || labels.length !== list.length) {
98
- console.warn(`Expected ${list.length} labels, got ${labels?.length || 0}`);
99
- // Fallback to default labels if parsing fails
100
- return { other: [...list] };
101
- }
102
-
103
- // Group items by their labels
104
- const result = {};
105
- labels.forEach((label, idx) => {
106
- const key = String(label).trim() || 'other';
107
- if (!result[key]) result[key] = [];
108
- result[key].push(list[idx]);
109
- });
110
-
111
- return result;
112
- }
@@ -1,35 +0,0 @@
1
- import listGroup from './index.js';
2
- import { vi, describe, it, expect } from 'vitest';
3
-
4
- vi.mock('../../lib/chatgpt/index.js', () => ({
5
- default: vi.fn(() => 'odd\neven\nodd'),
6
- }));
7
-
8
- const examples = [
9
- {
10
- inputs: {
11
- list: ['a', 'bb', 'ccc'],
12
- instructions: 'odd or even length',
13
- categories: ['odd', 'even'],
14
- },
15
- want: { result: { odd: ['a', 'ccc'], even: ['bb'] } },
16
- },
17
- ];
18
-
19
- describe('list-group verblet', () => {
20
- it('groups items using instructions', async () => {
21
- const result = await listGroup(['a', 'bb', 'ccc'], 'odd or even length', ['odd', 'even']);
22
- expect(result).toStrictEqual({ odd: ['a', 'ccc'], even: ['bb'] });
23
- });
24
-
25
- examples.forEach((example) => {
26
- it(example.inputs.instructions, async () => {
27
- const result = await listGroup(
28
- example.inputs.list,
29
- example.inputs.instructions,
30
- example.inputs.categories
31
- );
32
- expect(result).toStrictEqual(example.want.result);
33
- });
34
- });
35
- });
@@ -1,16 +0,0 @@
1
- {
2
- "$schema": "http://json-schema.org/draft-07/schema#",
3
- "type": "object",
4
- "properties": {
5
- "labels": {
6
- "type": "array",
7
- "description": "Array of group labels corresponding to input items in the same order",
8
- "items": {
9
- "type": "string",
10
- "description": "Group label for the corresponding item"
11
- }
12
- }
13
- },
14
- "required": ["labels"],
15
- "additionalProperties": false
16
- }
@@ -1,11 +0,0 @@
1
- # list-map
2
-
3
- Transform a list with a single ChatGPT call by providing mapping instructions.
4
- The function hides the boilerplate prompting so you only supply the instructions.
5
-
6
- ```javascript
7
- import { listMap } from '../../index.js';
8
-
9
- await listMap(['Budget smartphone', 'Luxury watch'], 'Write a short playful tagline');
10
- // => ['Affordable tech for everyone', 'Elegance that tells time']
11
- ```
@@ -1,15 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import listMap from './index.js';
3
- import { longTestTimeout } from '../../constants/common.js';
4
-
5
- describe('list-map examples', () => {
6
- it(
7
- 'maps creative slogans',
8
- async () => {
9
- const items = ['smart toaster', 'robot vacuum'];
10
- const result = await listMap(items, 'Give a playful marketing slogan');
11
- expect(result.length).toBe(2);
12
- },
13
- longTestTimeout
14
- );
15
- });
@@ -1,26 +0,0 @@
1
- import chatGPT from '../../lib/chatgpt/index.js';
2
- import wrapVariable from '../../prompts/wrap-variable.js';
3
-
4
- const buildPrompt = function (list, instructions) {
5
- const instructionsBlock = wrapVariable(instructions, { tag: 'instructions' });
6
- const listBlock = wrapVariable(list.join('\n'), { tag: 'list' });
7
- return `For each line in <list>, apply the <instructions> to transform it.\nReturn the same number of lines without numbering.\n\n${instructionsBlock}\n${listBlock}`;
8
- };
9
-
10
- export default async function listMap(list, instructions, config = {}) {
11
- const { llm, ...options } = config;
12
- const output = await chatGPT(buildPrompt(list, instructions), {
13
- modelOptions: { ...llm },
14
- ...options,
15
- });
16
- const lines = output
17
- .split('\n')
18
- .map((line) => line.trim())
19
- .filter((line) => line.length > 0);
20
- if (lines.length !== list.length) {
21
- throw new Error(
22
- `Batch output line count mismatch (expected ${list.length}, got ${lines.length})`
23
- );
24
- }
25
- return lines;
26
- }
@@ -1,17 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest';
2
- import listMap from './index.js';
3
-
4
- vi.mock('../../lib/chatgpt/index.js', () => ({
5
- default: vi.fn(async (prompt) => {
6
- const match = prompt.match(/<list>\n([\s\S]*?)\n<\/list>/);
7
- const lines = match ? match[1].split('\n') : [];
8
- return lines.map((l) => l.toUpperCase()).join('\n');
9
- }),
10
- }));
11
-
12
- describe('list-map verblet', () => {
13
- it('maps items using instructions', async () => {
14
- const result = await listMap(['alpha', 'beta'], 'uppercase');
15
- expect(result).toStrictEqual(['ALPHA', 'BETA']);
16
- });
17
- });
@@ -1,10 +0,0 @@
1
- # list-reduce
2
-
3
- Combine an accumulator value with a list of strings using custom instructions in a single ChatGPT call. Returns the final accumulator.
4
-
5
- ```javascript
6
- import listReduce from './index.js';
7
-
8
- await listReduce('', ['alpha', 'beta', 'gamma'], 'Concatenate them');
9
- // => 'alpha beta gamma'
10
- ```
@@ -1,14 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import listReduce from './index.js';
3
- import { longTestTimeout } from '../../constants/common.js';
4
-
5
- describe('list-reduce examples', () => {
6
- it(
7
- 'combines items with custom instructions',
8
- async () => {
9
- const result = await listReduce('', ['red', 'green', 'blue'], 'join with commas');
10
- expect(result.length).toBeGreaterThan(0);
11
- },
12
- longTestTimeout
13
- );
14
- });
@@ -1,21 +0,0 @@
1
- import chatGPT from '../../lib/chatgpt/index.js';
2
- import wrapVariable from '../../prompts/wrap-variable.js';
3
-
4
- function buildPrompt(acc, list, instructions) {
5
- const instructionsBlock = wrapVariable(instructions, {
6
- tag: 'instructions',
7
- forceHTML: true,
8
- });
9
- const accBlock = wrapVariable(acc, { tag: 'accumulator', forceHTML: true });
10
- const listBlock = wrapVariable(list.join('\n'), { tag: 'list' });
11
- return `Start with the given accumulator. Apply the <instructions> to each item in <list> sequentially, using the result as the new accumulator each time. Return only the final accumulator.\n\n${instructionsBlock}\n${accBlock}\n${listBlock}`;
12
- }
13
-
14
- export default async function listReduce(acc, list, instructions, config = {}) {
15
- const { llm, ...options } = config;
16
- const output = await chatGPT(buildPrompt(acc, list, instructions), {
17
- modelOptions: { ...llm },
18
- ...options,
19
- });
20
- return output.trim();
21
- }
@@ -1,27 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest';
2
- import listReduce from './index.js';
3
-
4
- vi.mock('../../lib/chatgpt/index.js', () => ({
5
- default: vi.fn((prompt) => {
6
- const listMatch = prompt.match(/<list>\n([\s\S]*?)\n<\/list>/);
7
- const accMatch = prompt.match(/<accumulator>\n([\s\S]*?)\n<\/accumulator>/);
8
- let lines = [];
9
-
10
- if (listMatch && accMatch) {
11
- lines = listMatch[1]
12
- .split('\n')
13
- .map((line) => line.trim())
14
- .filter(Boolean);
15
- return lines.join('+');
16
- }
17
- // Return just the joined list items
18
- return lines.join('+');
19
- }),
20
- }));
21
-
22
- describe('list-reduce verblet', () => {
23
- it('reduces items using instructions', async () => {
24
- const result = await listReduce('0', ['a', 'b', 'c'], 'join');
25
- expect(result).toBe('a+b+c');
26
- });
27
- });