@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
@@ -1,17 +1,18 @@
1
1
  // Basic
2
- export const asUndefinedByDefault =
3
- 'If you are unsure, say "undefined" as your answer.';
4
- export const asBool = `Answer the question either with "true" or "false" as your answer.`;
5
- export const asNumber = `Answer the question with a number that could be parsed by the JS Number constructor. Do not include formatting, units, digit group separators, or spelled-out numbers in your answer.`;
6
- export const asJSON = `Respond with a JSON object or array that parses with JSON.parse, with no wrapping code block, and no wrapping XML.`;
2
+ export const asUndefinedByDefault = 'If you are unsure, say "undefined" as your answer.';
3
+ export const asBool = 'Answer the question either with "true" or "false" as your answer.';
4
+ export const asNumber =
5
+ 'Answer the question with a number that could be parsed by the JS Number constructor. Do not include formatting, units, digit group separators, or spelled-out numbers in your answer.';
6
+ export const asDate =
7
+ 'Answer the question with a date that can be parsed by the JS Date constructor. ISO format is preferred. Do not include additional text or punctuation.';
8
+ export const asJSON =
9
+ 'Respond with a JSON object or array that parses with JSON.parse, with no wrapping code block, and no wrapping XML.';
7
10
 
8
11
  // Response steering
9
- export const useLineNumber =
10
- 'Include the line number where each check is performed.';
12
+ export const useLineNumber = 'Include the line number where each check is performed.';
11
13
  export const noFalseInformation = 'Do not include false information.';
12
14
  export const strictFormat = 'You MUST follow the format as described.';
13
- export const tryCompleteData =
14
- 'Err towards giving complete data, even if you have to guess.';
15
+ export const tryCompleteData = 'Err towards giving complete data, even if you have to guess.';
15
16
 
16
17
  // JSON Output
17
18
  export const onlyJSON =
@@ -20,6 +21,8 @@ const onlyJSONArrayBase =
20
21
  'Respond with a JSON array that parses with JSON.parse, with no additional text, no punctuation, and no code block.';
21
22
  export const onlyJSONArray = onlyJSONArrayBase;
22
23
  export const onlyJSONStringArray = `${onlyJSONArrayBase} The array should only contain text. No additional structure.`;
24
+ export const onlyJSONStringArrayPerLine =
25
+ 'For each input line, return exactly one line containing a JSON array of strings. No additional text, no code blocks.';
23
26
  export const onlyJSONObjectArray =
24
27
  'Return an array of obects--not strings, and not just the objects.';
25
28
  export const onlyJSONStringArrayAlt1 = 'Output an JSON array of strings.';
@@ -40,27 +43,29 @@ export const contentToJSON = 'Contents to convert to JSON:';
40
43
  export const contentIsExample = 'Use this as example output only:';
41
44
  export const contentIsChoices = 'Choose only from the following:';
42
45
  export const contentIsTransformationSource = 'Transform the following object:';
43
- export const contentListCriteria =
44
- 'Create a list of items with the following description:';
45
- export const contentListItemCriteria =
46
- 'Make sure each item meets the following conditions:';
46
+ export const contentListCriteria = 'Create a list of items with the following description:';
47
+ export const contentListItemCriteria = 'Make sure each item meets the following conditions:';
47
48
  export const contentListToOmit = 'Do not use any of the following items:';
48
49
  export const contentIsExampleObject =
49
50
  'The returned object must look like the following, including all the same properties:';
50
- export const contentIsSchema =
51
- 'Make it conform exactly to the following schema:';
51
+ export const contentIsSchema = 'Make it conform exactly to the following schema:';
52
52
  export const contentHasIntent = 'What is the intent of the following message:';
53
53
  export const contentIsSortCriteria = 'Sort the following items by:';
54
+ export const contentIsIntent = 'Give me an intent response for the following:';
55
+ export const contentIsOperationOption = 'The extracted operation must be one of the following:';
56
+ export const contentIsParametersOptions =
57
+ 'The extracted parameters must be from the following options:';
54
58
 
55
59
  // Give explanation
56
60
  export const explainAndSeparate =
57
61
  'Give an explanation followed by a succinct answer. The explanation part should come first, and should be at least 100 words. Next, insert a row of 20 equal signs (=) to create a clear separation.';
58
62
  export const explainAndSeparateJSON =
59
63
  'The content below the dividing line should only be valid JSON that can be parsed with JSON.parse.';
60
- export const explainAndSeparatePrimitive = `Next insert the succinctly-stated answer should be below the dividing line and work as a primitive datatype in JS. Be as succinct as possible as it will be parsed by a script.`;
64
+ export const explainAndSeparatePrimitive =
65
+ 'Next insert the succinctly-stated answer should be below the dividing line and work as a primitive datatype in JS. Be as succinct as possible as it will be parsed by a script.';
61
66
 
62
67
  // Reflective
63
- export const thinkStepByStep = `Let's think step by step`;
68
+ export const thinkStepByStep = "Let's think step by step";
64
69
  export const identifyUnclearInfo =
65
70
  'Identify any unclear or ambiguous information in your response, and rephrase it for clarity.';
66
71
  export const argueAgainstOutput =
@@ -76,10 +81,8 @@ export const summarizeRequest =
76
81
  'Please summarise what I am asking for you before you begin your answer.';
77
82
 
78
83
  // Analytical
79
- export const considerProsCons =
80
- 'Consider both pros and cons before arriving at a conclusion.';
81
- export const provideExamples =
82
- 'Provide specific examples to illustrate your point.';
84
+ export const considerProsCons = 'Consider both pros and cons before arriving at a conclusion.';
85
+ export const provideExamples = 'Provide specific examples to illustrate your point.';
83
86
  export const explainReasoning = 'Explain the reasoning behind your answer.';
84
87
  export const alternativeSolutions =
85
88
  'If there are any alternative solutions or perspectives, please share them.';
@@ -90,12 +93,9 @@ export const alternativeInterpretations = 'How else could this be interpreted?';
90
93
  // Evidence-Based
91
94
  export const evidenceSupportsView = 'What evidence supports your view?';
92
95
  export const expertResponse = 'How would an expert in this field respond?';
93
- export const limitationsOfApproach =
94
- 'What are the limitations of your approach?';
96
+ export const limitationsOfApproach = 'What are the limitations of your approach?';
95
97
  export const missingInformation = 'What information is still missing?';
96
98
  export const evaluateDifferingViews = 'How would you evaluate differing views?';
97
99
  export const confidenceInResponse = 'How confident are you in your response?';
98
- export const lessKnowledgeResponse =
99
- 'How would you answer this if you knew less about the topic?';
100
- export const analogyForUnderstanding =
101
- 'Come up with an analogy to make this easier to understand.';
100
+ export const lessKnowledgeResponse = 'How would you answer this if you knew less about the topic?';
101
+ export const analogyForUnderstanding = 'Come up with an analogy to make this easier to understand.';
@@ -1,7 +1,7 @@
1
1
  import {
2
+ contentListItemCriteria,
2
3
  onlyJSON,
3
4
  onlyJSONObjectArray,
4
- contentListItemCriteria,
5
5
  tryCompleteData,
6
6
  } from './constants.js';
7
7
  import asObjectWithSchema from './as-object-with-schema.js';
@@ -1,20 +1,20 @@
1
- import fs from 'node:fs/promises';
2
-
3
- import { contentIsExample, contentIsSchema, onlyJSON } from './constants.js';
1
+ import fs from 'fs/promises';
2
+ import { fileURLToPath } from 'url';
3
+ import { dirname, join } from 'path';
4
4
  import wrapVariable from './wrap-variable.js';
5
+ import { onlyJSON } from './constants.js';
6
+
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = dirname(__filename);
5
9
 
6
- const contentIsIntent = 'Give me an intent response for the following:';
7
- const contentIsOperationOption =
8
- 'The extracted operation must be one of the following:';
9
- const contentIsParametersOptions =
10
- 'The extracted perameters must be from the following options:';
10
+ const contentIsIntent = 'The intent is:';
11
+ const contentIsSchema = 'The schema is:';
12
+ const contentIsExample = 'An example of the output is:';
13
+ const contentIsOperationOption = 'The possible operations are:';
14
+ const contentIsParametersOptions = 'The possible parameters are:';
11
15
 
12
16
  const exampleJSON = `{
13
- "queryText": "play some music",
14
- "intent": {
15
- "operation": "play-music",
16
- "displayName": "Play Music"
17
- },
17
+ "intent": "play_music",
18
18
  "parameters": {
19
19
  "genre": "rock"
20
20
  },
@@ -23,9 +23,7 @@ const exampleJSON = `{
23
23
  }
24
24
  }`;
25
25
 
26
- const intentSchema = JSON.parse(
27
- await fs.readFile('./src/json-schemas/intent.json')
28
- );
26
+ const intentSchema = JSON.parse(await fs.readFile(join(__dirname, '../json-schemas/intent.json')));
29
27
 
30
28
  /**
31
29
  * Approximates intent recognition like you might find with Wit.ai,
@@ -35,15 +33,11 @@ const intentSchema = JSON.parse(
35
33
  export default (text, { operations = [], parameters = [] } = {}) => {
36
34
  let operationsSection = '';
37
35
  if (operations.length) {
38
- operationsSection = `\n${contentIsOperationOption} ${operations.join(
39
- ', '
40
- )}\n`;
36
+ operationsSection = `\n${contentIsOperationOption} ${operations.join(', ')}\n`;
41
37
  }
42
38
  let parametersSection = '';
43
39
  if (parameters.length) {
44
- parametersSection = `\n${contentIsParametersOptions} ${parameters.join(
45
- ', '
46
- )}\n`;
40
+ parametersSection = `\n${contentIsParametersOptions} ${parameters.join(', ')}\n`;
47
41
  }
48
42
 
49
43
  return `
@@ -12,7 +12,6 @@ import * as R from 'ramda';
12
12
  * const openEndedPrompt = `Questions ${openEndedDegree} open-ended. `
13
13
  */
14
14
  export default (value, thresholds = []) => {
15
- const threshold =
16
- thresholds.find((t) => value <= t.threshold) || R.last(thresholds);
15
+ const threshold = thresholds.find((t) => value <= t.threshold) || R.last(thresholds);
17
16
  return threshold.degree;
18
17
  };
@@ -1,9 +1,9 @@
1
1
  import wrapVariable from './wrap-variable.js';
2
2
  import {
3
- contentIsSortCriteria,
4
- contentIsMain,
5
- contentIsFixes,
6
3
  contentIsDetails,
4
+ contentIsFixes,
5
+ contentIsMain,
6
+ contentIsSortCriteria,
7
7
  onlyJSONStringArray,
8
8
  } from './constants.js';
9
9
 
@@ -12,11 +12,7 @@ export const defaultFixes = 'Ignore duplicates in the list';
12
12
  export const defaultSortOrder = 'descending';
13
13
 
14
14
  export default (
15
- {
16
- description = defaultSortDescription,
17
- fixes = defaultFixes,
18
- sortOrder = defaultSortOrder,
19
- },
15
+ { description = defaultSortDescription, fixes = defaultFixes, sortOrder = defaultSortOrder },
20
16
  list
21
17
  ) => {
22
18
  const listLines = JSON.stringify(list, undefined, 2);
@@ -12,17 +12,14 @@ export default (
12
12
  ) => {
13
13
  const toneModifiers = `Tone: ${tone.join(', ')}`;
14
14
  const vocabularyModifiers = `Vocabulary: ${vocabulary.join(', ')}`;
15
- const sentenceStructureModifiers = `Sentence structure: ${sentenceStructure.join(
16
- ', '
17
- )}`;
15
+ const sentenceStructureModifiers = `Sentence structure: ${sentenceStructure.join(', ')}`;
18
16
  const pointOfViewModifiers = `Point of view: ${pointOfView.join(', ')}`;
19
- const lengthModifier = `Use between ${minWords} and ${
20
- maxWords ?? 'any number'
21
- } of words`;
17
+ const lengthModifier = `Use between ${minWords} and ${maxWords ?? 'any number'} of words`;
22
18
 
23
19
  let noiseModifier = '';
24
20
  if (noise > 0.5) {
25
- noiseModifier = `Completely reshape the ideas here, don't stick with the original structure. Don't change the meaning of the content though.`;
21
+ noiseModifier =
22
+ "Completely reshape the ideas here, don't stick with the original structure. Don't change the meaning of the content though.";
26
23
  }
27
24
 
28
25
  return `Rewrite the following content:
@@ -1,9 +1,6 @@
1
1
  import wrapVariable from './wrap-variable.js';
2
2
 
3
- export default (
4
- list = [],
5
- { introText = 'Consider the following items:' } = {}
6
- ) => {
3
+ export default (list = [], { introText = 'Consider the following items:' } = {}) => {
7
4
  const listText = list.map((f, i) => ` - ${i + 1}. ${f}`).join('\n');
8
5
 
9
6
  let listFragment = wrapVariable('\n');
@@ -0,0 +1,432 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import fetch from 'node-fetch';
3
+ import modelService from './index.js';
4
+ import Model from './model.js';
5
+
6
+ import { run as chatgptRun } from '../../lib/chatgpt/index.js';
7
+
8
+ // Mock node-fetch before importing chatgpt
9
+ vi.mock('node-fetch', () => ({
10
+ default: vi.fn(),
11
+ }));
12
+
13
+ // Mock Redis
14
+ vi.mock('../../services/redis/index.js', () => ({
15
+ getClient: vi.fn().mockResolvedValue({
16
+ get: vi.fn().mockResolvedValue(null),
17
+ setex: vi.fn().mockResolvedValue('OK'),
18
+ }),
19
+ }));
20
+
21
+ // Mock the prompt cache to avoid Redis dependencies
22
+ vi.mock('../../lib/prompt-cache/index.js', () => ({
23
+ get: vi.fn().mockResolvedValue({ result: null }),
24
+ set: vi.fn().mockResolvedValue('OK'),
25
+ }));
26
+
27
+ // Helper tokenizer
28
+ const tokenizer = (t) => t.split(' ');
29
+
30
+ describe('Global Override System', () => {
31
+ beforeEach(() => {
32
+ // Reset models and overrides before each test
33
+ modelService.models = {};
34
+ modelService.clearGlobalOverride(); // Clear all overrides
35
+ modelService.bestPublicModelKey = 'fastGood';
36
+
37
+ // Setup basic models for testing
38
+ modelService.models = {
39
+ fastGood: new Model({
40
+ name: 'gpt-4-fast-good',
41
+ maxContextWindow: 128000,
42
+ maxOutputTokens: 16384,
43
+ requestTimeout: 1000,
44
+ apiUrl: 'https://api.openai.com',
45
+ apiKey: 'test-key',
46
+ endpoint: '/v1/chat/completions',
47
+ tokenizer,
48
+ }),
49
+ fastCheap: new Model({
50
+ name: 'gpt-4-fast-cheap',
51
+ maxContextWindow: 128000,
52
+ maxOutputTokens: 8192,
53
+ requestTimeout: 1000,
54
+ apiUrl: 'https://api.openai.com',
55
+ apiKey: 'test-key',
56
+ endpoint: '/v1/chat/completions',
57
+ tokenizer,
58
+ }),
59
+ fastCheapReasoning: new Model({
60
+ name: 'gpt-4-fast-cheap-reasoning',
61
+ maxContextWindow: 200000,
62
+ maxOutputTokens: 50000,
63
+ requestTimeout: 3000,
64
+ apiUrl: 'https://api.openai.com',
65
+ apiKey: 'reasoning-key',
66
+ endpoint: '/v1/chat/completions',
67
+ tokenizer,
68
+ }),
69
+ customModel: new Model({
70
+ name: 'custom-model-name',
71
+ maxContextWindow: 64000,
72
+ maxOutputTokens: 8192,
73
+ requestTimeout: 2000,
74
+ apiUrl: 'https://custom.api.com',
75
+ apiKey: 'custom-key',
76
+ endpoint: '/v1/completions',
77
+ tokenizer,
78
+ }),
79
+ expensiveReasoning: new Model({
80
+ name: 'gpt-4-reasoning',
81
+ maxContextWindow: 200000,
82
+ maxOutputTokens: 100000,
83
+ requestTimeout: 5000,
84
+ apiUrl: 'https://api.openai.com',
85
+ apiKey: 'reasoning-key',
86
+ endpoint: '/v1/chat/completions',
87
+ tokenizer,
88
+ }),
89
+ fastReasoning: new Model({
90
+ name: 'gpt-4-fast-reasoning',
91
+ maxContextWindow: 200000,
92
+ maxOutputTokens: 50000,
93
+ requestTimeout: 3000,
94
+ apiUrl: 'https://api.openai.com',
95
+ apiKey: 'reasoning-key',
96
+ endpoint: '/v1/chat/completions',
97
+ tokenizer,
98
+ }),
99
+ };
100
+
101
+ // Reset and setup fetch mock for each test
102
+ vi.clearAllMocks();
103
+ fetch.mockResolvedValue({
104
+ ok: true,
105
+ status: 200,
106
+ json: () =>
107
+ Promise.resolve({
108
+ choices: [{ message: { content: 'Test response' } }],
109
+ }),
110
+ });
111
+ });
112
+
113
+ afterEach(() => {
114
+ vi.clearAllMocks();
115
+ modelService.clearGlobalOverride(); // Clean up after each test
116
+ });
117
+
118
+ describe('Global Override Management', () => {
119
+ it('should set and get global overrides', () => {
120
+ modelService.setGlobalOverride('modelName', 'customModel');
121
+ modelService.setGlobalOverride('temperature', 0.8);
122
+
123
+ expect(modelService.getGlobalOverride('modelName')).toBe('customModel');
124
+ expect(modelService.getGlobalOverride('temperature')).toBe(0.8);
125
+ expect(modelService.getGlobalOverride('maxTokens')).toBe(null);
126
+ });
127
+
128
+ it('should get all global overrides', () => {
129
+ modelService.setGlobalOverride('modelName', 'customModel');
130
+ modelService.setGlobalOverride('temperature', 0.8);
131
+
132
+ const allOverrides = modelService.getAllGlobalOverrides();
133
+ expect(allOverrides.modelName).toBe('customModel');
134
+ expect(allOverrides.temperature).toBe(0.8);
135
+ expect(allOverrides.maxTokens).toBe(null);
136
+ });
137
+
138
+ it('should clear specific global overrides', () => {
139
+ modelService.setGlobalOverride('modelName', 'customModel');
140
+ modelService.setGlobalOverride('temperature', 0.8);
141
+
142
+ modelService.clearGlobalOverride('modelName');
143
+
144
+ expect(modelService.getGlobalOverride('modelName')).toBe(null);
145
+ expect(modelService.getGlobalOverride('temperature')).toBe(0.8);
146
+ });
147
+
148
+ it('should clear all global overrides', () => {
149
+ modelService.setGlobalOverride('modelName', 'customModel');
150
+ modelService.setGlobalOverride('temperature', 0.8);
151
+
152
+ modelService.clearGlobalOverride();
153
+
154
+ expect(modelService.getGlobalOverride('modelName')).toBe(null);
155
+ expect(modelService.getGlobalOverride('temperature')).toBe(null);
156
+ });
157
+
158
+ it('should throw error for invalid override keys', () => {
159
+ expect(() => {
160
+ modelService.setGlobalOverride('invalidKey', 'value');
161
+ }).toThrow('Invalid override key: invalidKey');
162
+
163
+ expect(() => {
164
+ modelService.clearGlobalOverride('invalidKey');
165
+ }).toThrow('Invalid override key: invalidKey');
166
+ });
167
+ });
168
+
169
+ describe('Model Name Override', () => {
170
+ it('should override model selection globally', async () => {
171
+ // Set global model override
172
+ modelService.setGlobalOverride('modelName', 'customModel');
173
+
174
+ const result = await chatgptRun('Test prompt', {
175
+ modelOptions: {
176
+ modelName: 'fastGood', // This should be overridden
177
+ },
178
+ });
179
+
180
+ expect(result).toBe('Test response');
181
+ expect(fetch).toHaveBeenCalledWith(
182
+ 'https://custom.api.com/v1/completions',
183
+ expect.objectContaining({
184
+ headers: {
185
+ Authorization: 'Bearer custom-key',
186
+ 'Content-Type': 'application/json',
187
+ },
188
+ body: expect.stringContaining('"model":"custom-model-name"'),
189
+ })
190
+ );
191
+ });
192
+
193
+ it('should override even when no modelOptions provided', async () => {
194
+ modelService.setGlobalOverride('modelName', 'expensiveReasoning');
195
+
196
+ const result = await chatgptRun('Test prompt');
197
+
198
+ expect(result).toBe('Test response');
199
+ expect(fetch).toHaveBeenCalledWith(
200
+ 'https://api.openai.com/v1/chat/completions',
201
+ expect.objectContaining({
202
+ headers: {
203
+ Authorization: 'Bearer reasoning-key',
204
+ 'Content-Type': 'application/json',
205
+ },
206
+ body: expect.stringContaining('"model":"gpt-4-reasoning"'),
207
+ })
208
+ );
209
+ });
210
+ });
211
+
212
+ describe('Negotiation Override', () => {
213
+ it('should override negotiation options globally', async () => {
214
+ modelService.setGlobalOverride('negotiate', { reasoning: true });
215
+
216
+ const result = await chatgptRun('Test prompt', {
217
+ modelOptions: {
218
+ modelName: 'fastGood',
219
+ negotiate: { fast: true, cheap: true }, // This should be overridden
220
+ },
221
+ });
222
+
223
+ expect(result).toBe('Test response');
224
+ // Should negotiate to reasoning model instead of fast+cheap
225
+ expect(fetch).toHaveBeenCalledWith(
226
+ 'https://api.openai.com/v1/chat/completions',
227
+ expect.objectContaining({
228
+ body: expect.stringContaining('"model":"gpt-4-fast-cheap-reasoning"'),
229
+ })
230
+ );
231
+ });
232
+
233
+ it('should apply negotiation when none provided in options', async () => {
234
+ modelService.setGlobalOverride('negotiate', { fast: true, cheap: true });
235
+
236
+ const result = await chatgptRun('Test prompt', {
237
+ modelOptions: {
238
+ modelName: 'expensiveReasoning', // Should be overridden by negotiation
239
+ },
240
+ });
241
+
242
+ expect(result).toBe('Test response');
243
+ // Should negotiate instead of using explicit model
244
+ expect(fetch).toHaveBeenCalledWith(
245
+ 'https://api.openai.com/v1/chat/completions',
246
+ expect.objectContaining({
247
+ body: expect.stringContaining('"model":"gpt-4-fast-cheap"'),
248
+ })
249
+ );
250
+ });
251
+ });
252
+
253
+ describe('Parameter Overrides', () => {
254
+ it('should override temperature globally', async () => {
255
+ modelService.setGlobalOverride('temperature', 0.9);
256
+
257
+ const result = await chatgptRun('Test prompt', {
258
+ modelOptions: {
259
+ modelName: 'fastGood',
260
+ temperature: 0.1, // This should be overridden
261
+ },
262
+ });
263
+
264
+ expect(result).toBe('Test response');
265
+ expect(fetch).toHaveBeenCalledWith(
266
+ 'https://api.openai.com/v1/chat/completions',
267
+ expect.objectContaining({
268
+ body: expect.stringContaining('"temperature":0.9'),
269
+ })
270
+ );
271
+ });
272
+
273
+ it('should override multiple parameters', async () => {
274
+ modelService.setGlobalOverride('temperature', 0.8);
275
+ modelService.setGlobalOverride('maxTokens', 1500);
276
+ modelService.setGlobalOverride('topP', 0.95);
277
+
278
+ const result = await chatgptRun('Test prompt', {
279
+ modelOptions: {
280
+ modelName: 'fastGood',
281
+ temperature: 0.1,
282
+ maxTokens: 500,
283
+ topP: 0.5,
284
+ },
285
+ });
286
+
287
+ expect(result).toBe('Test response');
288
+
289
+ const requestBody = JSON.parse(fetch.mock.calls[0][1].body);
290
+ expect(requestBody.temperature).toBe(0.8);
291
+ expect(requestBody.max_tokens).toBe(1500);
292
+ expect(requestBody.top_p).toBe(0.95);
293
+ });
294
+
295
+ it('should preserve non-overridden parameters', async () => {
296
+ modelService.setGlobalOverride('temperature', 0.8);
297
+
298
+ const result = await chatgptRun('Test prompt', {
299
+ modelOptions: {
300
+ modelName: 'fastGood',
301
+ temperature: 0.1,
302
+ maxTokens: 2000,
303
+ topP: 0.7,
304
+ },
305
+ });
306
+
307
+ expect(result).toBe('Test response');
308
+
309
+ const requestBody = JSON.parse(fetch.mock.calls[0][1].body);
310
+ expect(requestBody.temperature).toBe(0.8); // Overridden
311
+ expect(requestBody.max_tokens).toBe(2000); // Preserved
312
+ expect(requestBody.top_p).toBe(0.7); // Preserved
313
+ });
314
+ });
315
+
316
+ describe('Complex Override Scenarios', () => {
317
+ it('should combine model and parameter overrides', async () => {
318
+ modelService.setGlobalOverride('modelName', 'customModel');
319
+ modelService.setGlobalOverride('temperature', 0.9);
320
+
321
+ const result = await chatgptRun('Test prompt', {
322
+ modelOptions: {
323
+ modelName: 'fastGood',
324
+ temperature: 0.1,
325
+ maxTokens: 1000,
326
+ },
327
+ });
328
+
329
+ expect(result).toBe('Test response');
330
+ expect(fetch).toHaveBeenCalledWith(
331
+ 'https://custom.api.com/v1/completions',
332
+ expect.objectContaining({
333
+ headers: {
334
+ Authorization: 'Bearer custom-key',
335
+ 'Content-Type': 'application/json',
336
+ },
337
+ body: expect.stringContaining('"temperature":0.9'),
338
+ })
339
+ );
340
+
341
+ const requestBody = JSON.parse(fetch.mock.calls[0][1].body);
342
+ expect(requestBody.model).toBe('custom-model-name');
343
+ expect(requestBody.max_tokens).toBe(1000); // Preserved
344
+ });
345
+
346
+ it('should handle override precedence correctly', async () => {
347
+ // Set both model name and negotiation overrides
348
+ modelService.setGlobalOverride('modelName', 'customModel');
349
+ modelService.setGlobalOverride('negotiate', { reasoning: true });
350
+
351
+ const result = await chatgptRun('Test prompt', {
352
+ modelOptions: {
353
+ modelName: 'fastGood',
354
+ },
355
+ });
356
+
357
+ expect(result).toBe('Test response');
358
+ // Negotiation should take precedence and override the model name
359
+ expect(fetch).toHaveBeenCalledWith(
360
+ 'https://api.openai.com/v1/chat/completions',
361
+ expect.objectContaining({
362
+ body: expect.stringContaining('"model":"gpt-4-fast-cheap-reasoning"'),
363
+ })
364
+ );
365
+ });
366
+
367
+ it('should work with empty modelOptions', async () => {
368
+ modelService.setGlobalOverride('modelName', 'customModel');
369
+ modelService.setGlobalOverride('temperature', 0.7);
370
+
371
+ const result = await chatgptRun('Test prompt');
372
+
373
+ expect(result).toBe('Test response');
374
+ expect(fetch).toHaveBeenCalledWith(
375
+ 'https://custom.api.com/v1/completions',
376
+ expect.objectContaining({
377
+ headers: {
378
+ Authorization: 'Bearer custom-key',
379
+ 'Content-Type': 'application/json',
380
+ },
381
+ body: expect.stringContaining('"temperature":0.7'),
382
+ })
383
+ );
384
+ });
385
+ });
386
+
387
+ describe('Override Isolation', () => {
388
+ it('should not affect subsequent calls after clearing overrides', async () => {
389
+ // Set override and make a call
390
+ modelService.setGlobalOverride('modelName', 'customModel');
391
+
392
+ await chatgptRun('Test prompt 1');
393
+ expect(fetch).toHaveBeenCalledWith(
394
+ 'https://custom.api.com/v1/completions',
395
+ expect.anything()
396
+ );
397
+
398
+ // Clear override and make another call
399
+ modelService.clearGlobalOverride('modelName');
400
+ fetch.mockClear();
401
+
402
+ await chatgptRun('Test prompt 2');
403
+ expect(fetch).toHaveBeenCalledWith(
404
+ 'https://api.openai.com/v1/chat/completions',
405
+ expect.anything()
406
+ );
407
+ });
408
+
409
+ it('should apply overrides consistently across multiple calls', async () => {
410
+ modelService.setGlobalOverride('temperature', 0.8);
411
+
412
+ // First call
413
+ await chatgptRun('Test prompt 1', {
414
+ modelOptions: { modelName: 'fastGood', temperature: 0.1 },
415
+ });
416
+
417
+ // Second call
418
+ await chatgptRun('Test prompt 2', {
419
+ modelOptions: { modelName: 'customModel', temperature: 0.2 },
420
+ });
421
+
422
+ // Both calls should have temperature overridden to 0.8
423
+ expect(fetch).toHaveBeenCalledTimes(2);
424
+
425
+ const firstCall = JSON.parse(fetch.mock.calls[0][1].body);
426
+ const secondCall = JSON.parse(fetch.mock.calls[1][1].body);
427
+
428
+ expect(firstCall.temperature).toBe(0.8);
429
+ expect(secondCall.temperature).toBe(0.8);
430
+ });
431
+ });
432
+ });