@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
@@ -27,21 +27,15 @@ describe('List verblet', () => {
27
27
  examples.forEach((example) => {
28
28
  let jsonSchemaDisplay = '';
29
29
  if (example.inputs.jsonSchemaQuery) {
30
- const jsonSchemaEllipsis =
31
- example.inputs.jsonSchemaQuery.length > 10 ? '...' : '';
32
- jsonSchemaDisplay = ` - ${example.inputs.jsonSchemaQuery.slice(
33
- 0,
34
- 10
35
- )}${jsonSchemaEllipsis}`;
30
+ const jsonSchemaEllipsis = example.inputs.jsonSchemaQuery.length > 10 ? '...' : '';
31
+ jsonSchemaDisplay = ` - ${example.inputs.jsonSchemaQuery.slice(0, 10)}${jsonSchemaEllipsis}`;
36
32
  }
37
33
  it(
38
- `${example.inputs.text}${jsonSchemaDisplay}`,
34
+ `${example.inputs.description}${jsonSchemaDisplay}`,
39
35
  async () => {
40
36
  let schema;
41
37
  if (example.inputs.jsonSchemaQuery) {
42
- schema = await toObject(
43
- await chatGPT(asJSONSchema(example.inputs.schemaQuery))
44
- );
38
+ schema = await toObject(await chatGPT(asJSONSchema(example.inputs.jsonSchemaQuery)));
45
39
  }
46
40
 
47
41
  const result = await list(example.inputs.description, {
@@ -53,16 +47,18 @@ describe('List verblet', () => {
53
47
  }
54
48
 
55
49
  if (example.want.listContains) {
56
- expect(
57
- result.some((item) => item.includes(example.want.listContains))
58
- ).equals(true);
50
+ expect(result.some((item) => item.includes(example.want.listContains))).equals(true);
59
51
  }
60
52
 
61
53
  if (example.want.listModelContains) {
62
54
  expect(
63
- result.some((item) =>
64
- item.model.includes(example.want.listModelContains)
65
- )
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
+ })
66
62
  ).equals(true);
67
63
  }
68
64
  },
@@ -1,16 +1,58 @@
1
- /* eslint-disable no-await-in-loop */
2
-
3
- import { operationTimeoutMultiplier } from '../../constants/openai.js';
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { operationTimeoutMultiplier } from '../../constants/models.js';
4
5
  import chatGPT from '../../lib/chatgpt/index.js';
5
6
  import {
6
- constants as promptConstants,
7
7
  asObjectWithSchema as asObjectWithSchemaPrompt,
8
8
  generateList as generateListPrompt,
9
+ constants as promptConstants,
9
10
  } from '../../prompts/index.js';
10
11
  import modelService from '../../services/llm-model/index.js';
11
- import toObject from '../../verblets/to-object/index.js';
12
12
 
13
- const { onlyJSON, contentIsTransformationSource } = promptConstants;
13
+ const { onlyJSON, contentIsTransformationSource, onlyJSONArray } = promptConstants;
14
+
15
+ // Get the directory of this module
16
+ const __filename = fileURLToPath(import.meta.url);
17
+ const __dirname = path.dirname(__filename);
18
+
19
+ /**
20
+ * Load the JSON schema for list results
21
+ * @returns {Promise<Object>} JSON schema for validation
22
+ */
23
+ async function getListSchema() {
24
+ const schemaPath = path.join(__dirname, 'list-result.json');
25
+ return JSON.parse(await fs.readFile(schemaPath, 'utf8'));
26
+ }
27
+
28
+ /**
29
+ * Create model options for structured outputs
30
+ * @param {string|Object} llm - LLM model name or configuration object
31
+ * @returns {Promise<Object>} Model options for chatGPT
32
+ */
33
+ async function createModelOptions(llm = 'fastGoodCheap') {
34
+ const schema = await getListSchema();
35
+
36
+ const responseFormat = {
37
+ type: 'json_schema',
38
+ json_schema: {
39
+ name: 'list_result',
40
+ schema,
41
+ },
42
+ };
43
+
44
+ if (typeof llm === 'string') {
45
+ return {
46
+ modelName: llm,
47
+ response_format: responseFormat,
48
+ };
49
+ } else {
50
+ return {
51
+ ...llm,
52
+ response_format: responseFormat,
53
+ };
54
+ }
55
+ }
14
56
 
15
57
  const outputTransformPrompt = (result, schema) => {
16
58
  return `${contentIsTransformationSource} ${result}
@@ -28,22 +70,21 @@ const shouldStopDefault = ({ queryCount, startTime } = {}) => {
28
70
  return (
29
71
  queryCount > 5 ||
30
72
  new Date() - startTime >
31
- operationTimeoutMultiplier *
32
- modelService.getBestAvailableModel().requestTimeout
73
+ operationTimeoutMultiplier * modelService.getBestPublicModel().requestTimeout
33
74
  );
34
75
  };
35
76
 
36
- export const generateList = async function* generateListGenerator(
37
- text,
38
- options = {}
39
- ) {
77
+ export const generateList = async function* generateListGenerator(text, options = {}) {
40
78
  const resultsAll = [];
41
79
  const resultsAllMap = {};
42
80
  let isDone = false;
43
81
  const {
44
82
  shouldSkip = shouldSkipDefault,
45
83
  shouldStop = shouldStopDefault,
46
- model = modelService.getBestAvailableModel(),
84
+ model = 'fastGoodCheap',
85
+ // eslint-disable-next-line no-unused-vars
86
+ _schema,
87
+ ...passThroughOptions
47
88
  } = options;
48
89
 
49
90
  const startTime = new Date();
@@ -55,26 +96,27 @@ export const generateList = async function* generateListGenerator(
55
96
  existing: resultsAll,
56
97
  });
57
98
 
58
- const budget = model.budgetTokens(listPrompt);
59
-
60
99
  let resultsNew = [];
61
100
  try {
101
+ const modelOptions = await createModelOptions(model);
102
+ // eslint-disable-next-line no-await-in-loop
62
103
  const results = await chatGPT(listPrompt, {
63
- modelOptions: {
64
- maxTokens: budget.completion,
65
- },
66
- ...options,
104
+ modelOptions,
105
+ ...passThroughOptions,
67
106
  });
68
107
 
69
- // debug helper:
70
- // console.error(R.sort((a, b) => a.localeCompare(b), await toObject(results)));
71
-
72
- resultsNew = await toObject(results);
108
+ // With structured outputs, response should already be parsed and validated
109
+ const parsed = typeof results === 'string' ? JSON.parse(results) : results;
110
+ // Extract items from the object structure
111
+ const resultArray = parsed?.items || parsed;
112
+ resultsNew = Array.isArray(resultArray) ? resultArray.filter(Boolean) : [];
73
113
  } catch (error) {
74
114
  if (/The operation was aborted/.test(error.message)) {
115
+ // eslint-disable-next-line no-console
75
116
  console.error('Generate list [error]: Aborted');
76
117
  resultsNew = []; // continue
77
118
  } else {
119
+ // eslint-disable-next-line no-console
78
120
  console.error(
79
121
  `Generate list [error]: ${error.message}`,
80
122
  listPrompt.slice(0, 100).replace('\n', '\\n')
@@ -84,9 +126,7 @@ export const generateList = async function* generateListGenerator(
84
126
  }
85
127
  }
86
128
 
87
- const resultsNewUnique = resultsNew.filter(
88
- (item) => !(item in resultsAllMap)
89
- );
129
+ const resultsNewUnique = resultsNew.filter((item) => !(item in resultsAllMap));
90
130
 
91
131
  queryCount += 1;
92
132
 
@@ -99,11 +139,13 @@ export const generateList = async function* generateListGenerator(
99
139
  startTime,
100
140
  };
101
141
 
142
+ // eslint-disable-next-line no-await-in-loop
102
143
  if (await shouldStop(perResultControlFactors)) {
103
144
  isDone = true;
104
145
  break;
105
146
  }
106
147
 
148
+ // eslint-disable-next-line no-await-in-loop
107
149
  if (!(await shouldSkip(perResultControlFactors))) {
108
150
  resultsAllMap[result] = true;
109
151
  resultsAll.push(result);
@@ -123,39 +165,50 @@ export const generateList = async function* generateListGenerator(
123
165
  startTime,
124
166
  };
125
167
 
168
+ // eslint-disable-next-line no-await-in-loop
126
169
  if (await shouldStop(perQueryControlFactors)) {
127
170
  isDone = true;
128
171
  }
129
172
  }
130
173
  };
131
174
 
132
- export default async (text, options) => {
133
- const generator = generateList(text, options);
134
- const { schema, model = modelService.getBestAvailableModel() } =
135
- options ?? {};
136
-
137
- const results = [];
138
- for await (const result of generator) {
139
- results.push(result);
140
- }
141
-
142
- if (!schema) {
143
- return results;
144
- }
145
-
146
- const resultObjects = await Promise.all(
147
- results.map(async (result) => {
148
- const prompt = outputTransformPrompt(result, schema);
149
- const budget = model.budgetTokens(prompt);
150
-
151
- const resultObject = await chatGPT(prompt, {
152
- maxTokens: budget.completion,
175
+ export default async function list(prompt, config = {}) {
176
+ const { llm, schema, ...options } = config;
177
+ const fullPrompt = `${prompt}\n\n${onlyJSONArray}`;
178
+
179
+ const modelOptions = await createModelOptions(llm);
180
+ const response = await chatGPT(fullPrompt, {
181
+ modelOptions,
182
+ ...options,
183
+ });
184
+
185
+ // With structured outputs, response should already be parsed and validated
186
+ const result = typeof response === 'string' ? JSON.parse(response) : response;
187
+ // Extract items from the object structure
188
+ const resultArray = result?.items || result;
189
+ const items = Array.isArray(resultArray) ? resultArray : [];
190
+
191
+ // If schema is provided, transform each item to match the schema
192
+ if (schema && items.length > 0) {
193
+ const transformedItems = [];
194
+ for (const item of items) {
195
+ const transformPrompt = outputTransformPrompt(item, schema);
196
+ const transformResponse = await chatGPT(transformPrompt, {
197
+ modelOptions: {
198
+ ...llm,
199
+ },
153
200
  ...options,
154
201
  });
202
+ try {
203
+ const transformedItem = JSON.parse(transformResponse);
204
+ transformedItems.push(transformedItem);
205
+ } catch {
206
+ // If transformation fails, keep the original item
207
+ transformedItems.push(item);
208
+ }
209
+ }
210
+ return transformedItems;
211
+ }
155
212
 
156
- return toObject(resultObject);
157
- })
158
- );
159
-
160
- return resultObjects;
161
- };
213
+ return items;
214
+ }
@@ -1,13 +1,16 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import { describe, expect, it, vi } from 'vitest';
3
+ import { fileURLToPath } from 'url';
4
+ import { dirname, join } from 'path';
3
5
 
4
6
  import toObject from '../../verblets/to-object/index.js';
5
7
  import list from './index.js';
6
8
 
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = dirname(__filename);
11
+
7
12
  const loadSchema = async () => {
8
- const file = (
9
- await fs.readFile('./src/json-schemas/cars-test.json')
10
- ).toString();
13
+ const file = (await fs.readFile(join(__dirname, '../../json-schemas/cars-test.json'))).toString();
11
14
 
12
15
  return toObject(file);
13
16
  };
@@ -53,15 +56,11 @@ describe('List verblet', () => {
53
56
  });
54
57
 
55
58
  if (example.want.listContains) {
56
- expect(
57
- result.some((item) => example.want.listContains.test(item))
58
- ).equals(true);
59
+ expect(result.some((item) => example.want.listContains.test(item))).equals(true);
59
60
  }
60
61
 
61
62
  if (example.want.listModelContains) {
62
- expect(
63
- result.some((item) => example.want.listModelContains.test(item.model))
64
- ).equals(true);
63
+ expect(result.some((item) => example.want.listModelContains.test(item.model))).equals(true);
65
64
  }
66
65
  });
67
66
  });
@@ -0,0 +1,16 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "properties": {
5
+ "items": {
6
+ "type": "array",
7
+ "description": "Array of generated list items",
8
+ "items": {
9
+ "type": "string",
10
+ "description": "A generated list item"
11
+ }
12
+ }
13
+ },
14
+ "required": ["items"],
15
+ "additionalProperties": false
16
+ }
@@ -0,0 +1,208 @@
1
+ # LLM Logger
2
+
3
+ The LLM Logger is an **advanced logger implementation** that you can use with the global logger service. It provides sophisticated features like ring buffers, multi-lane processing, and file context tracking.
4
+
5
+ ## ⚠️ Important: Not Used Automatically
6
+
7
+ The LLM Logger is **NOT automatically used** by the application. You must:
8
+
9
+ 1. **Create an LLM Logger instance** using `createLLMLogger()`
10
+ 2. **Set it as the global logger** using `setLogger()`
11
+
12
+ The application uses a noop logger by default and will be silent unless you explicitly configure logging.
13
+
14
+ ## Basic Usage
15
+
16
+ ```javascript
17
+ import { setLogger } from 'verblets/lib/logger-service';
18
+ import { createLLMLogger, createConsoleWriter } from 'verblets/chains/llm-logger';
19
+
20
+ // Create an LLM logger instance
21
+ const llmLogger = createLLMLogger({
22
+ ringBufferSize: 1000,
23
+ lanes: [
24
+ {
25
+ laneId: 'console',
26
+ writer: createConsoleWriter('[LOG] ')
27
+ }
28
+ ]
29
+ });
30
+
31
+ // Set it as the global logger
32
+ setLogger(llmLogger);
33
+
34
+ // Now all verblets and chains will use this logger
35
+ import { bulkMap } from 'verblets';
36
+ const result = await bulkMap(items, processor); // Will log using LLM logger
37
+ ```
38
+
39
+ ## Features
40
+
41
+ - **Ring Buffer**: Memory-efficient circular buffer for log storage
42
+ - **Multi-lane Processing**: Route logs to different outputs based on filters
43
+ - **File Context Tracking**: Automatically captures file and line information
44
+ - **Batch Processing**: Efficient batching of log outputs
45
+ - **Advanced Filtering**: Custom filter functions for sophisticated log routing
46
+
47
+ ## Configuration
48
+
49
+ ### Basic Configuration
50
+
51
+ ```javascript
52
+ import { createLLMLogger, createConsoleWriter } from 'verblets/chains/llm-logger';
53
+
54
+ const logger = createLLMLogger({
55
+ ringBufferSize: 1000, // Size of the ring buffer
56
+ flushInterval: 100, // Flush interval in milliseconds
57
+ lanes: [ // Lane configurations
58
+ {
59
+ laneId: 'all',
60
+ writer: createConsoleWriter()
61
+ }
62
+ ]
63
+ });
64
+ ```
65
+
66
+ ### Multi-lane Configuration
67
+
68
+ ```javascript
69
+ const logger = createLLMLogger({
70
+ ringBufferSize: 5000,
71
+ lanes: [
72
+ {
73
+ laneId: 'errors',
74
+ filters: (log) => log.meta.get('level') === 'error',
75
+ writer: createConsoleWriter('[ERROR] ')
76
+ },
77
+ {
78
+ laneId: 'structured',
79
+ filters: (log) => typeof log.raw === 'object',
80
+ writer: createConsoleWriter('[STRUCT] ')
81
+ },
82
+ {
83
+ laneId: 'all',
84
+ writer: createFileWriter('/tmp/all.log')
85
+ }
86
+ ]
87
+ });
88
+ ```
89
+
90
+ ## Lane Configuration
91
+
92
+ Each lane can have:
93
+
94
+ - **`laneId`**: Unique identifier for the lane
95
+ - **`writer`**: Function that receives an array of log strings
96
+ - **`filters`** (optional): Function that determines if a log should be processed by this lane
97
+
98
+ ### Filter Function
99
+
100
+ The filter function receives a log entry object:
101
+
102
+ ```javascript
103
+ {
104
+ id: 'log_1234567890_abc123',
105
+ ts: 1234567890123,
106
+ raw: 'original log data',
107
+ fileContext: { filePath: '/path/to/file.js', line: 42 },
108
+ meta: Map { 'level' => 'info', 'fileContext' => {...} }
109
+ }
110
+ ```
111
+
112
+ ## Writers
113
+
114
+ ### Console Writer
115
+
116
+ ```javascript
117
+ import { createConsoleWriter } from 'verblets/chains/llm-logger';
118
+
119
+ const writer = createConsoleWriter('[PREFIX] ');
120
+ // Outputs: [PREFIX] log message
121
+ ```
122
+
123
+ ### File Writer
124
+
125
+ ```javascript
126
+ import { createFileWriter } from 'verblets/chains/llm-logger';
127
+
128
+ const writer = createFileWriter('/path/to/logfile.log');
129
+ // Currently a placeholder - shows: [FILE:/path/to/logfile.log] N lines
130
+ ```
131
+
132
+ ### Custom Writer
133
+
134
+ ```javascript
135
+ const customWriter = (logs) => {
136
+ logs.forEach(log => {
137
+ // Send to external service, database, etc.
138
+ sendToLogService(log);
139
+ });
140
+ };
141
+ ```
142
+
143
+ ## Advanced Usage
144
+
145
+ ### Accessing Ring Buffer
146
+
147
+ ```javascript
148
+ const logger = createLLMLogger({...});
149
+
150
+ // Get all logs
151
+ const allLogs = logger.ringBuffer.all();
152
+
153
+ // Get recent logs
154
+ const recentLogs = logger.ringBuffer.tail(10);
155
+
156
+ // Get oldest logs
157
+ const oldestLogs = logger.ringBuffer.head(5);
158
+ ```
159
+
160
+ ### Manual Flush
161
+
162
+ ```javascript
163
+ // Force flush all lanes immediately
164
+ logger.flush();
165
+ ```
166
+
167
+ ### Clear Logs
168
+
169
+ ```javascript
170
+ // Clear ring buffer and lane buffers
171
+ logger.clear();
172
+ ```
173
+
174
+ ## Integration with Global Logger Service
175
+
176
+ ```javascript
177
+ import { setLogger, log, info, warn, error } from 'verblets';
178
+ import { createLLMLogger, createConsoleWriter } from 'verblets/chains/llm-logger';
179
+
180
+ // Create and set LLM logger
181
+ const llmLogger = createLLMLogger({
182
+ lanes: [
183
+ {
184
+ laneId: 'console',
185
+ writer: createConsoleWriter()
186
+ }
187
+ ]
188
+ });
189
+
190
+ setLogger(llmLogger);
191
+
192
+ // Now you can use global logging functions
193
+ log('This goes through the LLM logger');
194
+ error('This is an error log');
195
+ ```
196
+
197
+ ## Examples
198
+
199
+ See `index.examples.js` for comprehensive examples including:
200
+
201
+ - Security monitoring systems
202
+ - Performance monitoring
203
+ - E-commerce transaction processing
204
+ - Bulk log analysis
205
+
206
+ ## Legacy API (Deprecated)
207
+
208
+ The old `initLogger()` and `log(data, logger)` functions are still available for backward compatibility but are deprecated. Use `createLLMLogger()` and the global logger service instead.