@far-world-labs/verblets 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (300) hide show
  1. package/.cursor/launch.json +30 -0
  2. package/.cursor/settings.json +20 -0
  3. package/.github/workflows/branch-protection.yml +22 -0
  4. package/.github/workflows/ci.yml +120 -0
  5. package/.prettierrc +6 -0
  6. package/.release-it.json +4 -1
  7. package/.vscode/launch.json +31 -0
  8. package/AGENTS.md +220 -0
  9. package/DEVELOPING.md +105 -0
  10. package/README.md +254 -0
  11. package/eslint.config.js +80 -0
  12. package/package.json +29 -17
  13. package/scripts/generate-test/index.js +29 -3
  14. package/scripts/runner/index.js +26 -0
  15. package/scripts/simple-editor/index.js +29 -18
  16. package/scripts/summarize-files/index.js +28 -4
  17. package/src/chains/README.md +30 -0
  18. package/src/chains/anonymize/README.md +21 -0
  19. package/src/chains/anonymize/index.examples.js +75 -0
  20. package/src/chains/anonymize/index.js +121 -0
  21. package/src/chains/anonymize/index.spec.js +78 -0
  22. package/src/chains/bulk-central-tendency/index.examples.js +138 -0
  23. package/src/chains/bulk-central-tendency/index.js +91 -0
  24. package/src/chains/bulk-filter/README.md +21 -0
  25. package/src/chains/bulk-filter/index.examples.js +22 -0
  26. package/src/chains/bulk-filter/index.js +58 -0
  27. package/src/chains/bulk-filter/index.spec.js +38 -0
  28. package/src/chains/bulk-find/README.md +16 -0
  29. package/src/chains/bulk-find/index.examples.js +20 -0
  30. package/src/chains/bulk-find/index.js +30 -0
  31. package/src/chains/bulk-find/index.spec.js +26 -0
  32. package/src/chains/bulk-group/README.md +23 -0
  33. package/src/chains/bulk-group/index.examples.js +18 -0
  34. package/src/chains/bulk-group/index.js +34 -0
  35. package/src/chains/bulk-group/index.spec.js +41 -0
  36. package/src/chains/bulk-map/README.md +43 -0
  37. package/src/chains/bulk-map/index.examples.js +17 -0
  38. package/src/chains/bulk-map/index.js +86 -0
  39. package/src/chains/bulk-map/index.spec.js +44 -0
  40. package/src/chains/bulk-reduce/README.md +12 -0
  41. package/src/chains/bulk-reduce/index.examples.js +15 -0
  42. package/src/chains/bulk-reduce/index.js +13 -0
  43. package/src/chains/bulk-reduce/index.spec.js +25 -0
  44. package/src/chains/bulk-score/README.md +16 -0
  45. package/src/chains/bulk-score/bulk-score-result.json +18 -0
  46. package/src/chains/bulk-score/index.examples.js +22 -0
  47. package/src/chains/bulk-score/index.js +133 -0
  48. package/src/chains/bulk-score/index.spec.js +30 -0
  49. package/src/chains/category-samples/README.md +61 -0
  50. package/src/chains/category-samples/index.examples.js +103 -0
  51. package/src/chains/category-samples/index.js +134 -0
  52. package/src/chains/collect-terms/README.md +12 -0
  53. package/src/chains/collect-terms/index.examples.js +16 -0
  54. package/src/chains/collect-terms/index.js +44 -0
  55. package/src/chains/collect-terms/index.spec.js +25 -0
  56. package/src/chains/date/README.md +12 -0
  57. package/src/chains/date/index.examples.js +47 -0
  58. package/src/chains/date/index.js +74 -0
  59. package/src/chains/date/index.spec.js +62 -0
  60. package/src/chains/disambiguate/README.md +22 -0
  61. package/src/chains/disambiguate/disambiguate-meanings-result.json +16 -0
  62. package/src/chains/disambiguate/index.examples.js +18 -0
  63. package/src/chains/disambiguate/index.js +92 -0
  64. package/src/chains/disambiguate/index.spec.js +25 -0
  65. package/src/chains/dismantle/README.md +67 -0
  66. package/src/chains/dismantle/dismantle.examples.js +27 -0
  67. package/src/chains/dismantle/index.js +6 -17
  68. package/src/chains/dismantle/index.spec.js +1 -2
  69. package/src/chains/expect/README.md +171 -0
  70. package/src/chains/expect/index.examples.js +146 -0
  71. package/src/chains/expect/index.js +173 -0
  72. package/src/chains/expect/index.spec.js +324 -0
  73. package/src/chains/filter-ambiguous/README.md +11 -0
  74. package/src/chains/filter-ambiguous/index.examples.js +20 -0
  75. package/src/chains/filter-ambiguous/index.js +49 -0
  76. package/src/chains/filter-ambiguous/index.spec.js +31 -0
  77. package/src/chains/glossary/README.md +19 -0
  78. package/src/chains/glossary/index.examples.js +386 -0
  79. package/src/chains/glossary/index.js +75 -0
  80. package/src/chains/glossary/index.spec.js +19 -0
  81. package/src/chains/intersections/README.md +152 -0
  82. package/src/chains/intersections/index.examples.js +279 -0
  83. package/src/chains/intersections/index.js +366 -0
  84. package/src/chains/intersections/intersection-result.json +38 -0
  85. package/src/chains/list/index.examples.js +12 -16
  86. package/src/chains/list/index.js +106 -53
  87. package/src/chains/list/index.spec.js +8 -9
  88. package/src/chains/list/list-result.json +16 -0
  89. package/src/chains/llm-logger/README.md +208 -0
  90. package/src/chains/llm-logger/index.js +205 -0
  91. package/src/chains/llm-logger/index.spec.js +330 -0
  92. package/src/chains/questions/index.examples.js +2 -1
  93. package/src/chains/questions/index.js +14 -15
  94. package/src/chains/scan-js/index.js +6 -9
  95. package/src/chains/set-interval/README.md +81 -0
  96. package/src/chains/set-interval/index.examples.js +36 -0
  97. package/src/chains/set-interval/index.js +131 -0
  98. package/src/chains/set-interval/index.spec.js +70 -0
  99. package/src/chains/socratic/README.md +17 -0
  100. package/src/chains/socratic/index.js +64 -0
  101. package/src/chains/socratic/index.spec.js +24 -0
  102. package/src/chains/sort/index.examples.js +3 -7
  103. package/src/chains/sort/index.js +65 -15
  104. package/src/chains/sort/index.spec.js +5 -8
  105. package/src/chains/sort/sort-result.json +16 -0
  106. package/src/chains/summary-map/README.md +9 -1
  107. package/src/chains/summary-map/index.examples.js +9 -2
  108. package/src/chains/summary-map/index.js +43 -25
  109. package/src/chains/summary-map/index.spec.js +78 -3
  110. package/src/chains/test/index.js +9 -13
  111. package/src/chains/test-advice/index.js +4 -5
  112. package/src/chains/themes/README.md +20 -0
  113. package/src/chains/themes/index.examples.js +17 -0
  114. package/src/chains/themes/index.js +28 -0
  115. package/src/chains/themes/index.spec.js +19 -0
  116. package/src/chains/veiled-variants/index.examples.js +18 -0
  117. package/src/chains/veiled-variants/index.js +107 -0
  118. package/src/chains/veiled-variants/index.spec.js +40 -0
  119. package/src/constants/common.js +0 -2
  120. package/src/constants/models.js +172 -0
  121. package/src/index.js +178 -18
  122. package/src/json-schemas/README.md +13 -0
  123. package/src/json-schemas/index.js +8 -14
  124. package/src/json-schemas/schema-dot-org-photograph.json +11 -5
  125. package/src/json-schemas/schema-dot-org-place.json +78 -5
  126. package/src/lib/README.md +26 -0
  127. package/src/lib/bulk-filter/README.md +22 -0
  128. package/src/lib/bulk-filter/index.examples.js +27 -0
  129. package/src/lib/bulk-filter/index.js +63 -0
  130. package/src/lib/bulk-filter/index.spec.js +38 -0
  131. package/src/lib/bulk-find/README.md +18 -0
  132. package/src/lib/bulk-find/index.examples.js +19 -0
  133. package/src/lib/bulk-find/index.js +30 -0
  134. package/src/lib/bulk-find/index.spec.js +41 -0
  135. package/src/lib/chatgpt/index.js +63 -43
  136. package/src/lib/combinations/index.js +30 -0
  137. package/src/lib/combinations/index.spec.js +23 -0
  138. package/src/lib/functional/index.js +28 -0
  139. package/src/lib/logger-service/index.js +32 -0
  140. package/src/lib/parse-js-parts/index.js +9 -21
  141. package/src/lib/parse-llm-list/README.md +39 -0
  142. package/src/lib/parse-llm-list/index.js +54 -0
  143. package/src/lib/parse-llm-list/index.spec.js +59 -0
  144. package/src/lib/path-aliases/index.js +1 -3
  145. package/src/lib/path-aliases/index.spec.js +2 -8
  146. package/src/lib/pave/index.js +4 -4
  147. package/src/lib/pave/index.spec.js +6 -3
  148. package/src/lib/prompt-cache/index.js +14 -10
  149. package/src/lib/retry/index.js +11 -8
  150. package/src/lib/ring-buffer/README.md +460 -0
  151. package/src/lib/ring-buffer/index.js +1074 -0
  152. package/src/lib/search-best-first/city-walk.spec.js +37 -0
  153. package/src/lib/search-best-first/index.js +42 -11
  154. package/src/lib/search-best-first/index.spec.js +35 -0
  155. package/src/lib/search-js-files/index.js +44 -47
  156. package/src/lib/search-js-files/scan-file.js +10 -21
  157. package/src/lib/shorten-text/index.js +2 -7
  158. package/src/lib/shorten-text/index.spec.js +3 -3
  159. package/src/lib/strip-response/index.js +2 -7
  160. package/src/lib/template-replace/index.js +23 -0
  161. package/src/lib/template-replace/index.spec.js +60 -0
  162. package/src/lib/to-date/index.js +11 -0
  163. package/src/lib/to-number/index.js +1 -1
  164. package/src/lib/transcribe/index.js +26 -9
  165. package/src/prompts/README.md +3 -1
  166. package/src/prompts/as-object-with-schema.js +3 -8
  167. package/src/prompts/as-schema-org-text.js +10 -2
  168. package/src/prompts/code-features.js +1 -5
  169. package/src/prompts/constants.js +27 -27
  170. package/src/prompts/generate-collection.js +1 -1
  171. package/src/prompts/intent.js +16 -22
  172. package/src/prompts/select-from-threshold.js +1 -2
  173. package/src/prompts/sort.js +4 -8
  174. package/src/prompts/style.js +4 -7
  175. package/src/prompts/wrap-list.js +1 -4
  176. package/src/services/llm-model/global-overrides.spec.js +432 -0
  177. package/src/services/llm-model/index.js +234 -40
  178. package/src/services/llm-model/model.js +2 -2
  179. package/src/services/llm-model/negotiate.spec.js +447 -0
  180. package/src/services/redis/index.js +70 -7
  181. package/src/test/setup.js +20 -0
  182. package/src/verblets/README.md +26 -0
  183. package/src/verblets/auto/index.examples.js +12 -9
  184. package/src/verblets/auto/index.js +10 -10
  185. package/src/verblets/auto/index.spec.js +4 -6
  186. package/src/verblets/bool/README.md +36 -0
  187. package/src/verblets/bool/index.examples.js +53 -1
  188. package/src/verblets/bool/index.js +6 -9
  189. package/src/verblets/bool/index.spec.js +1 -3
  190. package/src/verblets/central-tendency/README.md +166 -0
  191. package/src/verblets/central-tendency/central-tendency-result.json +24 -0
  192. package/src/verblets/central-tendency/index.examples.js +196 -0
  193. package/src/verblets/central-tendency/index.js +171 -0
  194. package/src/verblets/central-tendency/index.spec.js +148 -0
  195. package/src/verblets/enum/index.examples.js +1 -4
  196. package/src/verblets/enum/index.js +7 -4
  197. package/src/verblets/expect/README.md +64 -0
  198. package/src/verblets/expect/index.examples.js +109 -0
  199. package/src/verblets/expect/index.js +75 -0
  200. package/src/verblets/expect/index.spec.js +127 -0
  201. package/src/verblets/intent/index.examples.js +95 -7
  202. package/src/verblets/intent/index.js +56 -68
  203. package/src/verblets/intersection/README.md +16 -0
  204. package/src/verblets/intersection/index.examples.js +89 -0
  205. package/src/verblets/intersection/index.js +84 -0
  206. package/src/verblets/intersection/index.spec.js +60 -0
  207. package/src/verblets/intersection/intersection-result.json +16 -0
  208. package/src/verblets/list-expand/README.md +10 -0
  209. package/src/verblets/list-expand/index.examples.js +14 -0
  210. package/src/verblets/list-expand/index.js +104 -0
  211. package/src/verblets/list-expand/index.spec.js +18 -0
  212. package/src/verblets/list-expand/list-expand-result.json +16 -0
  213. package/src/verblets/list-filter/README.md +22 -0
  214. package/src/verblets/list-filter/index.examples.js +26 -0
  215. package/src/verblets/list-filter/index.js +18 -0
  216. package/src/verblets/list-filter/index.spec.js +19 -0
  217. package/src/verblets/list-find/README.md +11 -0
  218. package/src/verblets/list-find/index.examples.js +15 -0
  219. package/src/verblets/list-find/index.js +17 -0
  220. package/src/verblets/list-find/index.spec.js +19 -0
  221. package/src/verblets/list-group/README.md +16 -0
  222. package/src/verblets/list-group/index.examples.js +16 -0
  223. package/src/verblets/list-group/index.js +112 -0
  224. package/src/verblets/list-group/index.spec.js +35 -0
  225. package/src/verblets/list-group/list-group-result.json +16 -0
  226. package/src/verblets/list-map/README.md +11 -0
  227. package/src/verblets/list-map/index.examples.js +15 -0
  228. package/src/verblets/list-map/index.js +26 -0
  229. package/src/verblets/list-map/index.spec.js +17 -0
  230. package/src/verblets/list-reduce/README.md +10 -0
  231. package/src/verblets/list-reduce/index.examples.js +14 -0
  232. package/src/verblets/list-reduce/index.js +21 -0
  233. package/src/verblets/list-reduce/index.spec.js +27 -0
  234. package/src/verblets/list-reduce/index.spec.jsx +27 -0
  235. package/src/verblets/name/README.md +15 -0
  236. package/src/verblets/name/index.examples.js +28 -0
  237. package/src/verblets/name/index.js +19 -0
  238. package/src/verblets/name/index.spec.js +33 -0
  239. package/src/verblets/name-similar-to/README.md +26 -0
  240. package/src/verblets/name-similar-to/index.examples.js +18 -0
  241. package/src/verblets/name-similar-to/index.js +20 -0
  242. package/src/verblets/name-similar-to/index.spec.js +13 -0
  243. package/src/verblets/number/index.examples.js +173 -7
  244. package/src/verblets/number/index.js +5 -2
  245. package/src/verblets/number/index.spec.js +1 -3
  246. package/src/verblets/number-with-units/index.examples.js +5 -1
  247. package/src/verblets/number-with-units/index.js +74 -9
  248. package/src/verblets/number-with-units/number-with-units-result.json +23 -0
  249. package/src/verblets/schema-org/index.examples.js +2 -7
  250. package/src/verblets/schema-org/index.js +32 -3
  251. package/src/verblets/sentiment/README.md +10 -0
  252. package/src/verblets/sentiment/index.examples.js +20 -0
  253. package/src/verblets/sentiment/index.js +9 -0
  254. package/src/verblets/sentiment/index.spec.js +20 -0
  255. package/src/verblets/to-object/index.js +10 -15
  256. package/src/verblets/to-object/index.spec.js +1 -4
  257. package/.eslintrc.json +0 -42
  258. package/docs/README.md +0 -41
  259. package/docs/babel.config.js +0 -3
  260. package/docs/blog/2019-05-28-first-blog-post.md +0 -12
  261. package/docs/blog/2019-05-29-long-blog-post.md +0 -44
  262. package/docs/blog/2021-08-01-mdx-blog-post.mdx +0 -20
  263. package/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg +0 -0
  264. package/docs/blog/2021-08-26-welcome/index.md +0 -25
  265. package/docs/blog/authors.yml +0 -17
  266. package/docs/docs/api/bool.md +0 -74
  267. package/docs/docs/api/search.md +0 -51
  268. package/docs/docs/intro.md +0 -47
  269. package/docs/docs/tutorial-basics/_category_.json +0 -8
  270. package/docs/docs/tutorial-basics/congratulations.md +0 -23
  271. package/docs/docs/tutorial-basics/create-a-blog-post.md +0 -34
  272. package/docs/docs/tutorial-basics/create-a-document.md +0 -57
  273. package/docs/docs/tutorial-basics/create-a-page.md +0 -43
  274. package/docs/docs/tutorial-basics/deploy-your-site.md +0 -31
  275. package/docs/docs/tutorial-basics/markdown-features.mdx +0 -152
  276. package/docs/docs/tutorial-extras/_category_.json +0 -7
  277. package/docs/docs/tutorial-extras/img/docsVersionDropdown.png +0 -0
  278. package/docs/docs/tutorial-extras/img/localeDropdown.png +0 -0
  279. package/docs/docs/tutorial-extras/manage-docs-versions.md +0 -55
  280. package/docs/docs/tutorial-extras/translate-your-site.md +0 -88
  281. package/docs/docusaurus.config.js +0 -120
  282. package/docs/package.json +0 -44
  283. package/docs/sidebars.js +0 -31
  284. package/docs/src/components/HomepageFeatures/index.js +0 -61
  285. package/docs/src/components/HomepageFeatures/styles.module.css +0 -11
  286. package/docs/src/css/custom.css +0 -30
  287. package/docs/src/pages/index.js +0 -43
  288. package/docs/src/pages/index.module.css +0 -23
  289. package/docs/src/pages/markdown-page.md +0 -7
  290. package/docs/static/.nojekyll +0 -0
  291. package/docs/static/img/docusaurus-social-card.jpg +0 -0
  292. package/docs/static/img/docusaurus.png +0 -0
  293. package/docs/static/img/favicon.ico +0 -0
  294. package/docs/static/img/logo.svg +0 -1
  295. package/docs/static/img/undraw_docusaurus_mountain.svg +0 -171
  296. package/docs/static/img/undraw_docusaurus_react.svg +0 -170
  297. package/docs/static/img/undraw_docusaurus_tree.svg +0 -40
  298. package/src/constants/openai.js +0 -65
  299. /package/{.vite.config.examples.js → .vitest.config.examples.js} +0 -0
  300. /package/{.vite.config.js → .vitest.config.js} +0 -0
@@ -0,0 +1,205 @@
1
+ /**
2
+ * LLM Logger - Advanced Logging Implementation
3
+ *
4
+ * Creates a sophisticated logger instance that can be used with the global logger service.
5
+ * This is NOT automatically used - users must explicitly create and set it.
6
+ *
7
+ * Features:
8
+ * - Ring buffer for memory-efficient log storage
9
+ * - Multi-lane processing with custom filters
10
+ * - File context tracking
11
+ * - Batch processing capabilities
12
+ */
13
+
14
+ import RingBuffer from '../../lib/ring-buffer/index.js';
15
+
16
+ /**
17
+ * @typedef {Object} LogEntry
18
+ * @property {string} id - Unique identifier for the log entry
19
+ * @property {number} ts - Timestamp when the log was created
20
+ * @property {*} raw - The original log data
21
+ * @property {Object} fileContext - File context information
22
+ * @property {Map} meta - Additional metadata
23
+ */
24
+
25
+ /**
26
+ * @typedef {Object} LogLaneConfig
27
+ * @property {string} laneId - Unique identifier for the lane
28
+ * @property {Function} writer - Function to write logs (receives array of strings)
29
+ * @property {Function} [filters] - Optional filter function for log entries
30
+ */
31
+
32
+ /**
33
+ * Extract file context information from the call stack
34
+ */
35
+ function extractFileContext() {
36
+ const stack = new Error().stack;
37
+ const lines = stack.split('\n');
38
+
39
+ // Skip the first few lines (Error, extractFileContext, log function)
40
+ for (let i = 3; i < lines.length; i++) {
41
+ const line = lines[i];
42
+ const match = line.match(/at .* \((.+):(\d+):\d+\)/);
43
+ if (match) {
44
+ return {
45
+ filePath: match[1],
46
+ line: parseInt(match[2]),
47
+ };
48
+ }
49
+ }
50
+
51
+ return { filePath: 'unknown', line: 0 };
52
+ }
53
+
54
+ /**
55
+ * Create console writer function
56
+ */
57
+ export function createConsoleWriter(prefix = '') {
58
+ return (logs) => {
59
+ logs.forEach((log) => console.log(prefix + log));
60
+ };
61
+ }
62
+
63
+ /**
64
+ * Create file writer function (placeholder implementation)
65
+ */
66
+ export function createFileWriter(filePath) {
67
+ return (logs) => {
68
+ console.log(`[FILE:${filePath}] ${logs.length} lines`);
69
+ };
70
+ }
71
+
72
+ /**
73
+ * Create an LLM Logger instance
74
+ *
75
+ * @param {Object} config - Configuration object
76
+ * @param {number} [config.ringBufferSize=1000] - Size of the ring buffer
77
+ * @param {LogLaneConfig[]} [config.lanes=[]] - Lane configurations
78
+ * @param {number} [config.flushInterval=100] - Flush interval in milliseconds
79
+ * @returns {Object} Logger instance compatible with global logger service
80
+ */
81
+ export function createLLMLogger(config = {}) {
82
+ const { ringBufferSize = 1000, lanes = [], flushInterval = 100 } = config;
83
+
84
+ // Initialize ring buffer
85
+ const ringBuffer = new RingBuffer(ringBufferSize);
86
+
87
+ // Lane buffers for batching
88
+ const laneBuffers = new Map();
89
+ lanes.forEach((lane) => {
90
+ laneBuffers.set(lane.laneId, []);
91
+ });
92
+
93
+ // Flush loops for each lane
94
+ lanes.forEach((lane) => {
95
+ const flushLoop = () => {
96
+ const buffer = laneBuffers.get(lane.laneId);
97
+ if (buffer && buffer.length > 0) {
98
+ try {
99
+ lane.writer([...buffer]);
100
+ buffer.length = 0; // Clear buffer
101
+ } catch (error) {
102
+ console.error(`Error in lane ${lane.laneId}:`, error);
103
+ }
104
+ }
105
+ setTimeout(flushLoop, flushInterval);
106
+ };
107
+ flushLoop();
108
+ });
109
+
110
+ /**
111
+ * Process a log entry
112
+ */
113
+ function processLog(data, level = 'log') {
114
+ const logEntry = {
115
+ id: Date.now() + Math.random(),
116
+ ts: new Date(),
117
+ raw: data,
118
+ meta: new Map([
119
+ ['level', level],
120
+ ['fileContext', extractFileContext()],
121
+ ]),
122
+ };
123
+
124
+ // Add to ring buffer - the ring buffer will wrap this in its own structure
125
+ // but we need to store it so that ringBuffer.all()[0].data.raw works
126
+ // However, tests expect ringBuffer.all()[0].raw, so we need to modify the ring buffer behavior
127
+ // or adjust our approach. Let me store the logEntry directly and modify the ring buffer access.
128
+ ringBuffer.push(logEntry);
129
+
130
+ // Process through lanes - process each log individually
131
+ for (const lane of lanes) {
132
+ if (!lane.filters || lane.filters(logEntry)) {
133
+ const logString = typeof data === 'string' ? data : JSON.stringify(data);
134
+ laneBuffers.get(lane.laneId).push(logString);
135
+
136
+ // Trigger immediate flush for this lane if it has items
137
+ const buffer = laneBuffers.get(lane.laneId);
138
+ if (buffer.length > 0) {
139
+ lane.writer([...buffer]);
140
+ buffer.length = 0; // Clear buffer after writing
141
+ }
142
+ }
143
+ }
144
+ }
145
+
146
+ // Return logger instance compatible with global logger service
147
+ return {
148
+ // Standard logger interface
149
+ log: (data) => processLog(data, 'log'),
150
+ info: (data) => processLog(data, 'info'),
151
+ warn: (data) => processLog(data, 'warn'),
152
+ error: (data) => processLog(data, 'error'),
153
+ debug: (data) => processLog(data, 'debug'),
154
+ trace: (data) => processLog(data, 'trace'),
155
+ fatal: (data) => processLog(data, 'fatal'),
156
+
157
+ // Ring buffer access - need to map the data property to preserve logEntry structure
158
+ ringBuffer: {
159
+ all: () => ringBuffer.all().map((entry) => entry.data),
160
+ size: () => ringBuffer.size(),
161
+ clear: () => ringBuffer.clear(),
162
+ tail: (count) => ringBuffer.tail(count).map((entry) => entry.data),
163
+ head: (count) => ringBuffer.head(count).map((entry) => entry.data),
164
+ filter: (predicate) =>
165
+ ringBuffer.filter((entry) => predicate(entry.data)).map((entry) => entry.data),
166
+ },
167
+
168
+ // Utility methods
169
+ flush: () => {
170
+ for (const [laneId, buffer] of laneBuffers) {
171
+ if (buffer.length > 0) {
172
+ const lane = lanes.find((l) => l.laneId === laneId);
173
+ if (lane) {
174
+ lane.writer([...buffer]);
175
+ buffer.length = 0;
176
+ }
177
+ }
178
+ }
179
+ },
180
+
181
+ clear: () => {
182
+ ringBuffer.clear();
183
+ for (const buffer of laneBuffers.values()) {
184
+ buffer.length = 0;
185
+ }
186
+ },
187
+
188
+ getConfig: () => ({
189
+ ringBufferSize,
190
+ flushInterval,
191
+ lanes: [...lanes],
192
+ }),
193
+ };
194
+ }
195
+
196
+ // Legacy exports for backward compatibility (deprecated)
197
+ export const initLogger = createLLMLogger;
198
+ export const log = (data, logger) => {
199
+ if (logger && typeof logger.log === 'function') {
200
+ return logger.log(data);
201
+ }
202
+ console.warn(
203
+ 'LLM Logger: log() called without proper logger instance. Use createLLMLogger() and setLogger().'
204
+ );
205
+ };
@@ -0,0 +1,330 @@
1
+ import { beforeEach, describe, expect, it, vi, afterEach } from 'vitest';
2
+ import { createLLMLogger, createConsoleWriter, createFileWriter } from './index.js';
3
+ import { setLogger, resetLogger } from '../../lib/logger-service/index.js';
4
+
5
+ // Mock console methods
6
+ const mockConsoleLog = vi.fn();
7
+ const mockConsoleError = vi.fn();
8
+
9
+ beforeEach(() => {
10
+ vi.clearAllMocks();
11
+ console.log = mockConsoleLog;
12
+ console.error = mockConsoleError;
13
+ resetLogger(); // Start with noop logger
14
+ });
15
+
16
+ afterEach(() => {
17
+ vi.restoreAllMocks();
18
+ resetLogger();
19
+ });
20
+
21
+ describe('LLM Logger - Factory Pattern', () => {
22
+ describe('Logger Creation', () => {
23
+ it('should create logger with default configuration', () => {
24
+ const logger = createLLMLogger();
25
+
26
+ expect(logger).toHaveProperty('log');
27
+ expect(logger).toHaveProperty('info');
28
+ expect(logger).toHaveProperty('warn');
29
+ expect(logger).toHaveProperty('error');
30
+ expect(logger).toHaveProperty('debug');
31
+ expect(logger).toHaveProperty('trace');
32
+ expect(logger).toHaveProperty('fatal');
33
+ expect(logger).toHaveProperty('ringBuffer');
34
+ expect(logger).toHaveProperty('flush');
35
+ expect(logger).toHaveProperty('clear');
36
+ });
37
+
38
+ it('should create logger with custom configuration', () => {
39
+ const logger = createLLMLogger({
40
+ ringBufferSize: 500,
41
+ flushInterval: 50,
42
+ lanes: [
43
+ {
44
+ laneId: 'test',
45
+ writer: createConsoleWriter('[TEST] '),
46
+ },
47
+ ],
48
+ });
49
+
50
+ const config = logger.getConfig();
51
+ expect(config.ringBufferSize).toBe(500);
52
+ expect(config.flushInterval).toBe(50);
53
+ expect(config.lanes).toHaveLength(1);
54
+ expect(config.lanes[0].laneId).toBe('test');
55
+ });
56
+ });
57
+
58
+ describe('Global Logger Service Integration', () => {
59
+ it('should work as global logger', async () => {
60
+ const logger = createLLMLogger({
61
+ lanes: [
62
+ {
63
+ laneId: 'console',
64
+ writer: createConsoleWriter('[GLOBAL] '),
65
+ },
66
+ ],
67
+ });
68
+
69
+ setLogger(logger);
70
+
71
+ // Use global logger service methods
72
+ const { log, info, error } = await import('../../lib/logger-service/index.js');
73
+
74
+ log('test log');
75
+ info('test info');
76
+ error('test error');
77
+
78
+ // Allow flush loops to complete
79
+ await new Promise((resolve) => setTimeout(resolve, 150));
80
+
81
+ expect(mockConsoleLog).toHaveBeenCalledWith('[GLOBAL] test log');
82
+ expect(mockConsoleLog).toHaveBeenCalledWith('[GLOBAL] test info');
83
+ expect(mockConsoleLog).toHaveBeenCalledWith('[GLOBAL] test error');
84
+ });
85
+ });
86
+
87
+ describe('Ring Buffer Operations', () => {
88
+ it('should store logs in ring buffer', () => {
89
+ const logger = createLLMLogger({
90
+ ringBufferSize: 10,
91
+ lanes: [],
92
+ });
93
+
94
+ logger.log('test 1');
95
+ logger.info('test 2');
96
+ logger.error('test 3');
97
+
98
+ const allLogs = logger.ringBuffer.all();
99
+ expect(allLogs).toHaveLength(3);
100
+ expect(allLogs[0].raw).toBe('test 1');
101
+ expect(allLogs[1].raw).toBe('test 2');
102
+ expect(allLogs[2].raw).toBe('test 3');
103
+ });
104
+
105
+ it('should handle ring buffer overflow', () => {
106
+ const logger = createLLMLogger({
107
+ ringBufferSize: 2,
108
+ lanes: [],
109
+ });
110
+
111
+ logger.log('test 1');
112
+ logger.log('test 2');
113
+ logger.log('test 3'); // Should evict 'test 1'
114
+
115
+ const allLogs = logger.ringBuffer.all();
116
+ expect(allLogs).toHaveLength(2);
117
+ expect(allLogs[0].raw).toBe('test 2');
118
+ expect(allLogs[1].raw).toBe('test 3');
119
+ });
120
+ });
121
+
122
+ describe('Lane Processing', () => {
123
+ it('should process logs through multiple lanes', async () => {
124
+ const errorWriter = vi.fn();
125
+ const infoWriter = vi.fn();
126
+
127
+ const logger = createLLMLogger({
128
+ lanes: [
129
+ {
130
+ laneId: 'errors',
131
+ filters: (log) => log.meta.get('level') === 'error',
132
+ writer: errorWriter,
133
+ },
134
+ {
135
+ laneId: 'info',
136
+ filters: (log) => log.meta.get('level') === 'info',
137
+ writer: infoWriter,
138
+ },
139
+ ],
140
+ });
141
+
142
+ logger.error('error message');
143
+ logger.info('info message');
144
+ logger.debug('debug message'); // Should not match any lane
145
+
146
+ // Allow flush loops to complete
147
+ await new Promise((resolve) => setTimeout(resolve, 150));
148
+
149
+ expect(errorWriter).toHaveBeenCalledWith(['error message']);
150
+ expect(infoWriter).toHaveBeenCalledWith(['info message']);
151
+ });
152
+
153
+ it('should handle lanes without filters', async () => {
154
+ const allWriter = vi.fn();
155
+
156
+ const logger = createLLMLogger({
157
+ lanes: [
158
+ {
159
+ laneId: 'all',
160
+ writer: allWriter, // No filters - should receive all logs
161
+ },
162
+ ],
163
+ });
164
+
165
+ logger.log('string log');
166
+ logger.info({ type: 'object log' });
167
+
168
+ // Allow flush loops to complete
169
+ await new Promise((resolve) => setTimeout(resolve, 150));
170
+
171
+ expect(allWriter).toHaveBeenCalledTimes(2);
172
+ expect(allWriter).toHaveBeenCalledWith(['string log']);
173
+ expect(allWriter).toHaveBeenCalledWith(['{"type":"object log"}']);
174
+ });
175
+ });
176
+
177
+ describe('File Context Tracking', () => {
178
+ it('should capture file context for log entries', () => {
179
+ const logger = createLLMLogger();
180
+
181
+ logger.log('test with context');
182
+
183
+ const logs = logger.ringBuffer.all();
184
+ expect(logs).toHaveLength(1);
185
+
186
+ const logEntry = logs[0];
187
+ expect(logEntry.meta.has('fileContext')).toBe(true);
188
+
189
+ const fileContext = logEntry.meta.get('fileContext');
190
+ expect(fileContext).toHaveProperty('filePath');
191
+ expect(fileContext).toHaveProperty('line');
192
+ expect(typeof fileContext.line).toBe('number');
193
+ });
194
+ });
195
+
196
+ describe('Utility Methods', () => {
197
+ it('should flush all lanes manually', async () => {
198
+ const writer = vi.fn();
199
+
200
+ const logger = createLLMLogger({
201
+ lanes: [
202
+ {
203
+ laneId: 'test',
204
+ writer,
205
+ },
206
+ ],
207
+ });
208
+
209
+ logger.log('test message');
210
+
211
+ // Manual flush
212
+ logger.flush();
213
+
214
+ expect(writer).toHaveBeenCalledWith(['test message']);
215
+ });
216
+
217
+ it('should clear ring buffer and lane buffers', () => {
218
+ const logger = createLLMLogger();
219
+
220
+ logger.log('test 1');
221
+ logger.log('test 2');
222
+
223
+ expect(logger.ringBuffer.size()).toBe(2);
224
+
225
+ logger.clear();
226
+
227
+ expect(logger.ringBuffer.size()).toBe(0);
228
+ });
229
+ });
230
+
231
+ describe('Writer Functions', () => {
232
+ it('console writer outputs with prefix', () => {
233
+ const writer = createConsoleWriter('[TEST] ');
234
+ writer(['message 1', 'message 2']);
235
+
236
+ expect(mockConsoleLog).toHaveBeenCalledWith('[TEST] message 1');
237
+ expect(mockConsoleLog).toHaveBeenCalledWith('[TEST] message 2');
238
+ });
239
+
240
+ it('file writer shows placeholder output', () => {
241
+ const writer = createFileWriter('/tmp/test.log');
242
+ writer(['line 1', 'line 2', 'line 3']);
243
+
244
+ expect(mockConsoleLog).toHaveBeenCalledWith('[FILE:/tmp/test.log] 3 lines');
245
+ });
246
+ });
247
+
248
+ describe('Legacy API (Deprecated)', () => {
249
+ it('should warn when using legacy log function', async () => {
250
+ const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
251
+
252
+ // Import the legacy log function
253
+ const { log } = await import('./index.js');
254
+ log('test', null);
255
+
256
+ expect(consoleSpy).toHaveBeenCalledWith(
257
+ 'LLM Logger: log() called without proper logger instance. Use createLLMLogger() and setLogger().'
258
+ );
259
+
260
+ consoleSpy.mockRestore();
261
+ });
262
+
263
+ it('should work with legacy log function when logger provided', async () => {
264
+ const mockLogger = {
265
+ log: vi.fn(),
266
+ };
267
+
268
+ const { log } = await import('./index.js');
269
+ log('test message', mockLogger);
270
+
271
+ expect(mockLogger.log).toHaveBeenCalledWith('test message');
272
+ });
273
+ });
274
+
275
+ describe('Complex Scenarios', () => {
276
+ it('should handle mixed data types', async () => {
277
+ const writer = vi.fn();
278
+
279
+ const logger = createLLMLogger({
280
+ lanes: [
281
+ {
282
+ laneId: 'mixed',
283
+ writer,
284
+ },
285
+ ],
286
+ });
287
+
288
+ logger.log('string');
289
+ logger.log({ object: 'data' });
290
+ logger.log(123);
291
+ logger.log(null);
292
+
293
+ // Allow flush loops to complete
294
+ await new Promise((resolve) => setTimeout(resolve, 150));
295
+
296
+ expect(writer).toHaveBeenCalledWith(['string']);
297
+ expect(writer).toHaveBeenCalledWith(['{"object":"data"}']);
298
+ expect(writer).toHaveBeenCalledWith(['123']);
299
+ expect(writer).toHaveBeenCalledWith(['null']);
300
+ });
301
+
302
+ it('should handle high-volume logging', async () => {
303
+ const writer = vi.fn();
304
+
305
+ const logger = createLLMLogger({
306
+ ringBufferSize: 100,
307
+ lanes: [
308
+ {
309
+ laneId: 'volume',
310
+ writer,
311
+ },
312
+ ],
313
+ });
314
+
315
+ // Generate many logs
316
+ for (let i = 0; i < 50; i++) {
317
+ logger.log(`message ${i}`);
318
+ }
319
+
320
+ // Allow flush loops to complete
321
+ await new Promise((resolve) => setTimeout(resolve, 200));
322
+
323
+ // Should have processed all logs
324
+ expect(writer).toHaveBeenCalledTimes(50);
325
+
326
+ // Ring buffer should contain all logs (within capacity)
327
+ expect(logger.ringBuffer.size()).toBe(50);
328
+ });
329
+ });
330
+ });
@@ -8,6 +8,7 @@ import questions from './index.js';
8
8
  const ensureDirectoryExists = async (directoryPath) => {
9
9
  try {
10
10
  await fs.access(directoryPath);
11
+ // eslint-disable-next-line no-unused-vars
11
12
  } catch (error) {
12
13
  await fs.mkdir(directoryPath, { recursive: true });
13
14
  }
@@ -17,7 +18,7 @@ const readFileOrUndefined = async (filePath) => {
17
18
  let result;
18
19
  try {
19
20
  result = (await fs.readFile(filePath)).toString();
20
- // eslint-disable-next-line no-empty
21
+ // eslint-disable-next-line no-unused-vars
21
22
  } catch (error) {
22
23
  // do nothing
23
24
  }
@@ -1,11 +1,9 @@
1
- /* eslint-disable no-await-in-loop */
2
-
3
1
  import * as R from 'ramda';
4
2
 
5
3
  import chatGPT from '../../lib/chatgpt/index.js';
6
4
  import {
7
- constants as promptConstants,
8
5
  generateQuestions as generateQuestionsPrompt,
6
+ constants as promptConstants,
9
7
  } from '../../prompts/index.js';
10
8
  import modelService from '../../services/llm-model/index.js';
11
9
  import toObject from '../../verblets/to-object/index.js';
@@ -40,10 +38,7 @@ const shouldStopNull = (result, resultsAll, resultsNew, attempts = 0) => {
40
38
  return resultsAll.length > 50 || attempts > 5;
41
39
  };
42
40
 
43
- const generateQuestions = async function* generateQuestionsGenerator(
44
- text,
45
- options = {}
46
- ) {
41
+ const generateQuestions = async function* generateQuestionsGenerator(text, options = {}) {
47
42
  const resultsAll = [];
48
43
  const resultsAllMap = {};
49
44
  const drilldownResults = [];
@@ -54,7 +49,7 @@ const generateQuestions = async function* generateQuestionsGenerator(
54
49
  searchBreadth = 0.5,
55
50
  shouldSkip = shouldSkipNull,
56
51
  shouldStop = shouldStopNull,
57
- model = modelService.getBestAvailableModel(),
52
+ model = modelService.getBestPublicModel(),
58
53
  } = options;
59
54
 
60
55
  let attempts = 0;
@@ -63,10 +58,10 @@ const generateQuestions = async function* generateQuestionsGenerator(
63
58
  const choices = resultsAll.filter((item) => {
64
59
  return !drilldownResults.includes(item);
65
60
  });
66
- const pickInterestingQuestionPrompt = pickInterestingQuestion(
67
- textSelected,
68
- { existing: choices }
69
- );
61
+ const pickInterestingQuestionPrompt = pickInterestingQuestion(textSelected, {
62
+ existing: choices,
63
+ });
64
+ // eslint-disable-next-line no-await-in-loop
70
65
  textSelected = await chatGPT(pickInterestingQuestionPrompt);
71
66
  drilldownResults.push(textSelected);
72
67
  }
@@ -82,16 +77,20 @@ const generateQuestions = async function* generateQuestionsGenerator(
82
77
  },
83
78
  };
84
79
 
80
+ // eslint-disable-next-line no-await-in-loop
85
81
  const results = await chatGPT(`${promptCreated}`, chatGPTConfig);
86
82
  let resultsParsed;
87
83
  try {
84
+ // eslint-disable-next-line no-await-in-loop
88
85
  resultsParsed = await toObject(results);
89
86
  } catch (error) {
90
87
  if (/Unexpected string in JSON/.test(error.message)) {
88
+ // eslint-disable-next-line no-await-in-loop
91
89
  const resultsUpdated = await chatGPT(
92
90
  `${asSplitIntoJSONArray}${onlyJSON} \`\`\`${results}\`\`\``,
93
91
  chatGPTConfig
94
92
  );
93
+ // eslint-disable-next-line no-await-in-loop
95
94
  resultsParsed = await toObject(resultsUpdated);
96
95
  }
97
96
  }
@@ -100,17 +99,17 @@ const generateQuestions = async function* generateQuestionsGenerator(
100
99
  const randomIndex = Math.floor(Math.random() * resultsNew.length);
101
100
  textSelected = resultsNew[randomIndex];
102
101
  }
103
- const resultsNewUnique = resultsNew.filter(
104
- (item) => !(item in resultsAllMap)
105
- );
102
+ const resultsNewUnique = resultsNew.filter((item) => !(item in resultsAllMap));
106
103
 
107
104
  attempts += 1;
108
105
 
109
106
  for (const result of resultsNewUnique) {
107
+ // eslint-disable-next-line no-await-in-loop
110
108
  if (await shouldStop(result, resultsAll, resultsNew, attempts)) {
111
109
  isDone = true;
112
110
  break;
113
111
  }
112
+ // eslint-disable-next-line no-await-in-loop
114
113
  if (!(await shouldSkip(result, resultsAll))) {
115
114
  resultsAllMap[result] = true;
116
115
  resultsAll.push(result);
@@ -14,10 +14,7 @@ import toObject from '../../verblets/to-object/index.js';
14
14
 
15
15
  const codeFeatureDefinitions = JSON.parse(
16
16
  await fs.readFile(
17
- new URL(
18
- '../../lib/search-js-files/code-features-property-definitions.json',
19
- import.meta.url
20
- ),
17
+ new URL('../../lib/search-js-files/code-features-property-definitions.json', import.meta.url),
21
18
  'utf-8'
22
19
  )
23
20
  );
@@ -46,7 +43,7 @@ const visit = async ({
46
43
  extremeK: 4,
47
44
  },
48
45
  codeFeatureDefinitions.map((d) => d.criteria),
49
- modelService.getModel('textDavinci003')
46
+ modelService.getBestAvailableModel()
50
47
  );
51
48
  const sortCriteria = sortResults.slice(0, 5);
52
49
  const features = codeFeatureDefinitions.filter((def) => {
@@ -71,7 +68,7 @@ const visit = async ({
71
68
  await retry(async () => {
72
69
  const results = await chatGPT(visitPrompt, {
73
70
  modelOptions: {
74
- modelName: 'gpt35Turbo',
71
+ modelName: 'fastGood',
75
72
  },
76
73
  });
77
74
 
@@ -87,9 +84,9 @@ const visit = async ({
87
84
  const idDisplay = (state.pathAliases[id] ?? id).slice(-50).padStart(50);
88
85
 
89
86
  console.error(
90
- `${`${state.nodesFound}`.padEnd(3, ' ')} ${idDisplay}: ${organizeResult(
91
- resultParsed
92
- ).join(', ')}`
87
+ `${`${state.nodesFound}`.padEnd(3, ' ')} ${idDisplay}: ${organizeResult(resultParsed).join(
88
+ ', '
89
+ )}`
93
90
  );
94
91
  });
95
92