@far-world-labs/verblets 0.2.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (330) hide show
  1. package/README.md +86 -213
  2. package/dist/index.browser.js +74 -0
  3. package/dist/index.js +548 -0
  4. package/dist/shared-C6kPWghF.js +7806 -0
  5. package/package.json +32 -11
  6. package/.cursor/launch.json +0 -30
  7. package/.cursor/settings.json +0 -20
  8. package/.github/workflows/branch-protection.yml +0 -22
  9. package/.github/workflows/ci.yml +0 -165
  10. package/.husky/pre-commit +0 -4
  11. package/.prettierrc +0 -6
  12. package/.release-it.json +0 -12
  13. package/.vitest.config.examples.js +0 -12
  14. package/.vitest.config.js +0 -8
  15. package/.vscode/launch.json +0 -31
  16. package/AGENTS.md +0 -220
  17. package/DEVELOPING.md +0 -105
  18. package/docker-compose.yml +0 -7
  19. package/eslint.config.js +0 -80
  20. package/scripts/clear-redis.js +0 -74
  21. package/scripts/generate-chain/index.js +0 -111
  22. package/scripts/generate-lib/index.js +0 -68
  23. package/scripts/generate-test/index.js +0 -137
  24. package/scripts/generate-verblet/README.md +0 -17
  25. package/scripts/generate-verblet/index.js +0 -110
  26. package/scripts/run.sh +0 -15
  27. package/scripts/runner/index.js +0 -56
  28. package/scripts/simple-editor/README.md +0 -34
  29. package/scripts/simple-editor/index.js +0 -79
  30. package/scripts/summarize-files/index.js +0 -70
  31. package/src/chains/README.md +0 -30
  32. package/src/chains/anonymize/README.md +0 -21
  33. package/src/chains/anonymize/index.examples.js +0 -75
  34. package/src/chains/anonymize/index.js +0 -121
  35. package/src/chains/anonymize/index.spec.js +0 -78
  36. package/src/chains/bulk-central-tendency/index.examples.js +0 -138
  37. package/src/chains/bulk-central-tendency/index.js +0 -91
  38. package/src/chains/bulk-filter/README.md +0 -21
  39. package/src/chains/bulk-filter/index.examples.js +0 -22
  40. package/src/chains/bulk-filter/index.js +0 -58
  41. package/src/chains/bulk-filter/index.spec.js +0 -38
  42. package/src/chains/bulk-find/README.md +0 -16
  43. package/src/chains/bulk-find/index.examples.js +0 -20
  44. package/src/chains/bulk-find/index.js +0 -30
  45. package/src/chains/bulk-find/index.spec.js +0 -26
  46. package/src/chains/bulk-group/README.md +0 -23
  47. package/src/chains/bulk-group/index.examples.js +0 -18
  48. package/src/chains/bulk-group/index.js +0 -34
  49. package/src/chains/bulk-group/index.spec.js +0 -41
  50. package/src/chains/bulk-map/README.md +0 -43
  51. package/src/chains/bulk-map/index.examples.js +0 -17
  52. package/src/chains/bulk-map/index.js +0 -86
  53. package/src/chains/bulk-map/index.spec.js +0 -44
  54. package/src/chains/bulk-reduce/README.md +0 -12
  55. package/src/chains/bulk-reduce/index.examples.js +0 -15
  56. package/src/chains/bulk-reduce/index.js +0 -13
  57. package/src/chains/bulk-reduce/index.spec.js +0 -25
  58. package/src/chains/bulk-score/README.md +0 -16
  59. package/src/chains/bulk-score/bulk-score-result.json +0 -18
  60. package/src/chains/bulk-score/index.examples.js +0 -22
  61. package/src/chains/bulk-score/index.js +0 -133
  62. package/src/chains/bulk-score/index.spec.js +0 -30
  63. package/src/chains/category-samples/README.md +0 -61
  64. package/src/chains/category-samples/index.examples.js +0 -103
  65. package/src/chains/category-samples/index.js +0 -134
  66. package/src/chains/collect-terms/README.md +0 -12
  67. package/src/chains/collect-terms/index.examples.js +0 -16
  68. package/src/chains/collect-terms/index.js +0 -44
  69. package/src/chains/collect-terms/index.spec.js +0 -25
  70. package/src/chains/conversation/README.md +0 -26
  71. package/src/chains/conversation/index.examples.js +0 -398
  72. package/src/chains/conversation/index.js +0 -126
  73. package/src/chains/conversation/index.spec.js +0 -148
  74. package/src/chains/conversation/turn-policies.js +0 -93
  75. package/src/chains/conversation/turn-policies.md +0 -123
  76. package/src/chains/conversation/turn-policies.spec.js +0 -135
  77. package/src/chains/date/README.md +0 -12
  78. package/src/chains/date/index.examples.js +0 -47
  79. package/src/chains/date/index.js +0 -74
  80. package/src/chains/date/index.spec.js +0 -62
  81. package/src/chains/disambiguate/README.md +0 -22
  82. package/src/chains/disambiguate/disambiguate-meanings-result.json +0 -16
  83. package/src/chains/disambiguate/index.examples.js +0 -18
  84. package/src/chains/disambiguate/index.js +0 -92
  85. package/src/chains/disambiguate/index.spec.js +0 -25
  86. package/src/chains/dismantle/README.md +0 -67
  87. package/src/chains/dismantle/dismantle.examples.js +0 -27
  88. package/src/chains/dismantle/index.examples.js +0 -30
  89. package/src/chains/dismantle/index.js +0 -303
  90. package/src/chains/dismantle/index.spec.js +0 -32
  91. package/src/chains/expect/README.md +0 -171
  92. package/src/chains/expect/index.examples.js +0 -146
  93. package/src/chains/expect/index.js +0 -207
  94. package/src/chains/expect/index.spec.js +0 -324
  95. package/src/chains/filter-ambiguous/README.md +0 -11
  96. package/src/chains/filter-ambiguous/index.examples.js +0 -20
  97. package/src/chains/filter-ambiguous/index.js +0 -49
  98. package/src/chains/filter-ambiguous/index.spec.js +0 -31
  99. package/src/chains/glossary/README.md +0 -19
  100. package/src/chains/glossary/index.examples.js +0 -386
  101. package/src/chains/glossary/index.js +0 -75
  102. package/src/chains/glossary/index.spec.js +0 -19
  103. package/src/chains/intersections/README.md +0 -166
  104. package/src/chains/intersections/index.examples.js +0 -280
  105. package/src/chains/intersections/index.js +0 -218
  106. package/src/chains/intersections/intersection-result.json +0 -38
  107. package/src/chains/list/index.examples.js +0 -68
  108. package/src/chains/list/index.js +0 -214
  109. package/src/chains/list/index.spec.js +0 -67
  110. package/src/chains/list/list-result.json +0 -16
  111. package/src/chains/list/schema.json +0 -24
  112. package/src/chains/llm-logger/README.md +0 -366
  113. package/src/chains/llm-logger/index.js +0 -591
  114. package/src/chains/llm-logger/index.spec.js +0 -391
  115. package/src/chains/llm-logger/schema.json +0 -105
  116. package/src/chains/questions/index.examples.js +0 -69
  117. package/src/chains/questions/index.js +0 -135
  118. package/src/chains/questions/index.spec.js +0 -29
  119. package/src/chains/scan-js/index.js +0 -116
  120. package/src/chains/set-interval/README.md +0 -81
  121. package/src/chains/set-interval/index.examples.js +0 -64
  122. package/src/chains/set-interval/index.js +0 -152
  123. package/src/chains/set-interval/index.spec.js +0 -70
  124. package/src/chains/socratic/README.md +0 -17
  125. package/src/chains/socratic/index.js +0 -64
  126. package/src/chains/socratic/index.spec.js +0 -24
  127. package/src/chains/sort/index.examples.js +0 -36
  128. package/src/chains/sort/index.js +0 -163
  129. package/src/chains/sort/index.spec.js +0 -112
  130. package/src/chains/sort/sort-result.json +0 -16
  131. package/src/chains/summary-map/README.md +0 -41
  132. package/src/chains/summary-map/index.examples.js +0 -64
  133. package/src/chains/summary-map/index.js +0 -226
  134. package/src/chains/summary-map/index.spec.js +0 -153
  135. package/src/chains/test/index.js +0 -114
  136. package/src/chains/test-advice/index.js +0 -35
  137. package/src/chains/themes/README.md +0 -20
  138. package/src/chains/themes/index.examples.js +0 -17
  139. package/src/chains/themes/index.js +0 -28
  140. package/src/chains/themes/index.spec.js +0 -19
  141. package/src/chains/veiled-variants/index.examples.js +0 -18
  142. package/src/chains/veiled-variants/index.js +0 -107
  143. package/src/chains/veiled-variants/index.spec.js +0 -40
  144. package/src/constants/common.js +0 -13
  145. package/src/constants/messages.js +0 -3
  146. package/src/constants/models.js +0 -184
  147. package/src/index.js +0 -203
  148. package/src/json-schemas/README.md +0 -13
  149. package/src/json-schemas/cars-test.json +0 -11
  150. package/src/json-schemas/index.js +0 -12
  151. package/src/json-schemas/intent.json +0 -38
  152. package/src/json-schemas/schema-dot-org-photograph.json +0 -133
  153. package/src/json-schemas/schema-dot-org-place.json +0 -129
  154. package/src/lib/README.md +0 -26
  155. package/src/lib/any-signal/index.js +0 -28
  156. package/src/lib/assert/README.md +0 -84
  157. package/src/lib/assert/index.js +0 -50
  158. package/src/lib/bulk-filter/README.md +0 -22
  159. package/src/lib/bulk-filter/index.examples.js +0 -27
  160. package/src/lib/bulk-filter/index.js +0 -63
  161. package/src/lib/bulk-filter/index.spec.js +0 -38
  162. package/src/lib/bulk-find/README.md +0 -18
  163. package/src/lib/bulk-find/index.examples.js +0 -19
  164. package/src/lib/bulk-find/index.js +0 -30
  165. package/src/lib/bulk-find/index.spec.js +0 -41
  166. package/src/lib/chatgpt/index.js +0 -163
  167. package/src/lib/combinations/index.js +0 -30
  168. package/src/lib/combinations/index.spec.js +0 -23
  169. package/src/lib/editor/index.js +0 -31
  170. package/src/lib/functional/index.js +0 -28
  171. package/src/lib/logger-service/index.js +0 -32
  172. package/src/lib/parse-js-parts/index.js +0 -321
  173. package/src/lib/parse-js-parts/index.spec.js +0 -156
  174. package/src/lib/parse-llm-list/README.md +0 -39
  175. package/src/lib/parse-llm-list/index.js +0 -54
  176. package/src/lib/parse-llm-list/index.spec.js +0 -59
  177. package/src/lib/path-aliases/index.js +0 -37
  178. package/src/lib/path-aliases/index.spec.js +0 -64
  179. package/src/lib/pave/index.js +0 -34
  180. package/src/lib/pave/index.spec.js +0 -76
  181. package/src/lib/prompt-cache/index.js +0 -50
  182. package/src/lib/retry/index.js +0 -66
  183. package/src/lib/retry/index.spec.js +0 -86
  184. package/src/lib/ring-buffer/README.md +0 -82
  185. package/src/lib/ring-buffer/index.js +0 -235
  186. package/src/lib/ring-buffer/index.spec.js +0 -388
  187. package/src/lib/search-best-first/city-walk.spec.js +0 -37
  188. package/src/lib/search-best-first/index.js +0 -97
  189. package/src/lib/search-best-first/index.spec.js +0 -35
  190. package/src/lib/search-js-files/code-features-property-definitions.json +0 -123
  191. package/src/lib/search-js-files/index.examples.js +0 -22
  192. package/src/lib/search-js-files/index.js +0 -155
  193. package/src/lib/search-js-files/index.spec.js +0 -34
  194. package/src/lib/search-js-files/scan-file.js +0 -242
  195. package/src/lib/shorten-text/index.js +0 -25
  196. package/src/lib/shorten-text/index.spec.js +0 -68
  197. package/src/lib/strip-numeric/index.js +0 -5
  198. package/src/lib/strip-response/index.js +0 -30
  199. package/src/lib/template-replace/index.js +0 -23
  200. package/src/lib/template-replace/index.spec.js +0 -60
  201. package/src/lib/timed-abort-controller/index.js +0 -41
  202. package/src/lib/to-bool/index.js +0 -8
  203. package/src/lib/to-date/index.js +0 -11
  204. package/src/lib/to-enum/index.js +0 -14
  205. package/src/lib/to-number/index.js +0 -12
  206. package/src/lib/to-number-with-units/index.js +0 -51
  207. package/src/lib/transcribe/index.js +0 -78
  208. package/src/prompts/README.md +0 -17
  209. package/src/prompts/as-enum.js +0 -5
  210. package/src/prompts/as-json-schema.js +0 -9
  211. package/src/prompts/as-object-with-schema.js +0 -26
  212. package/src/prompts/as-schema-org-text.js +0 -25
  213. package/src/prompts/as-schema-org-type.js +0 -1
  214. package/src/prompts/blog-post.js +0 -7
  215. package/src/prompts/code-features.js +0 -24
  216. package/src/prompts/constants.js +0 -101
  217. package/src/prompts/features-json-schema.js +0 -27
  218. package/src/prompts/generate-collection.js +0 -26
  219. package/src/prompts/generate-list.js +0 -48
  220. package/src/prompts/generate-questions.js +0 -19
  221. package/src/prompts/index.js +0 -20
  222. package/src/prompts/intent.js +0 -60
  223. package/src/prompts/output-succinct-names.js +0 -3
  224. package/src/prompts/select-from-threshold.js +0 -17
  225. package/src/prompts/sort.js +0 -31
  226. package/src/prompts/style.js +0 -38
  227. package/src/prompts/summarize.js +0 -13
  228. package/src/prompts/token-budget.js +0 -3
  229. package/src/prompts/wrap-list.js +0 -11
  230. package/src/prompts/wrap-variable.js +0 -36
  231. package/src/services/llm-model/global-overrides.spec.js +0 -432
  232. package/src/services/llm-model/index.js +0 -308
  233. package/src/services/llm-model/model.js +0 -21
  234. package/src/services/llm-model/negotiate.spec.js +0 -447
  235. package/src/services/redis/index.js +0 -147
  236. package/src/test/setup.js +0 -20
  237. package/src/verblets/README.md +0 -26
  238. package/src/verblets/auto/index.examples.js +0 -31
  239. package/src/verblets/auto/index.js +0 -28
  240. package/src/verblets/auto/index.spec.js +0 -32
  241. package/src/verblets/bool/README.md +0 -36
  242. package/src/verblets/bool/index.examples.js +0 -80
  243. package/src/verblets/bool/index.js +0 -25
  244. package/src/verblets/bool/index.schema.json +0 -14
  245. package/src/verblets/bool/index.spec.js +0 -33
  246. package/src/verblets/central-tendency/README.md +0 -166
  247. package/src/verblets/central-tendency/central-tendency-result.json +0 -24
  248. package/src/verblets/central-tendency/index.examples.js +0 -196
  249. package/src/verblets/central-tendency/index.js +0 -171
  250. package/src/verblets/central-tendency/index.spec.js +0 -148
  251. package/src/verblets/conversation-turn/README.md +0 -33
  252. package/src/verblets/conversation-turn/index.examples.js +0 -218
  253. package/src/verblets/conversation-turn/index.js +0 -68
  254. package/src/verblets/conversation-turn/index.spec.js +0 -77
  255. package/src/verblets/conversation-turn-multi/README.md +0 -31
  256. package/src/verblets/conversation-turn-multi/index.examples.js +0 -160
  257. package/src/verblets/conversation-turn-multi/index.js +0 -104
  258. package/src/verblets/conversation-turn-multi/index.spec.js +0 -63
  259. package/src/verblets/enum/index.examples.js +0 -30
  260. package/src/verblets/enum/index.js +0 -18
  261. package/src/verblets/enum/index.spec.js +0 -35
  262. package/src/verblets/expect/README.md +0 -64
  263. package/src/verblets/expect/index.examples.js +0 -109
  264. package/src/verblets/expect/index.js +0 -75
  265. package/src/verblets/expect/index.spec.js +0 -127
  266. package/src/verblets/intent/index.examples.js +0 -139
  267. package/src/verblets/intent/index.js +0 -60
  268. package/src/verblets/intent/index.spec.js +0 -31
  269. package/src/verblets/intersection/README.md +0 -16
  270. package/src/verblets/intersection/index.examples.js +0 -89
  271. package/src/verblets/intersection/index.js +0 -125
  272. package/src/verblets/intersection/index.spec.js +0 -60
  273. package/src/verblets/intersection/intersection-result.json +0 -16
  274. package/src/verblets/list-expand/README.md +0 -10
  275. package/src/verblets/list-expand/index.examples.js +0 -14
  276. package/src/verblets/list-expand/index.js +0 -104
  277. package/src/verblets/list-expand/index.spec.js +0 -18
  278. package/src/verblets/list-expand/list-expand-result.json +0 -16
  279. package/src/verblets/list-filter/README.md +0 -22
  280. package/src/verblets/list-filter/index.examples.js +0 -26
  281. package/src/verblets/list-filter/index.js +0 -18
  282. package/src/verblets/list-filter/index.spec.js +0 -19
  283. package/src/verblets/list-find/README.md +0 -11
  284. package/src/verblets/list-find/index.examples.js +0 -15
  285. package/src/verblets/list-find/index.js +0 -17
  286. package/src/verblets/list-find/index.spec.js +0 -19
  287. package/src/verblets/list-group/README.md +0 -16
  288. package/src/verblets/list-group/index.examples.js +0 -16
  289. package/src/verblets/list-group/index.js +0 -112
  290. package/src/verblets/list-group/index.spec.js +0 -35
  291. package/src/verblets/list-group/list-group-result.json +0 -16
  292. package/src/verblets/list-map/README.md +0 -11
  293. package/src/verblets/list-map/index.examples.js +0 -15
  294. package/src/verblets/list-map/index.js +0 -26
  295. package/src/verblets/list-map/index.spec.js +0 -17
  296. package/src/verblets/list-reduce/README.md +0 -10
  297. package/src/verblets/list-reduce/index.examples.js +0 -14
  298. package/src/verblets/list-reduce/index.js +0 -21
  299. package/src/verblets/list-reduce/index.spec.js +0 -27
  300. package/src/verblets/list-reduce/index.spec.jsx +0 -27
  301. package/src/verblets/name/README.md +0 -15
  302. package/src/verblets/name/index.examples.js +0 -28
  303. package/src/verblets/name/index.js +0 -19
  304. package/src/verblets/name/index.spec.js +0 -33
  305. package/src/verblets/name-similar-to/README.md +0 -26
  306. package/src/verblets/name-similar-to/index.examples.js +0 -18
  307. package/src/verblets/name-similar-to/index.js +0 -20
  308. package/src/verblets/name-similar-to/index.spec.js +0 -13
  309. package/src/verblets/number/index.examples.js +0 -199
  310. package/src/verblets/number/index.js +0 -25
  311. package/src/verblets/number/index.spec.js +0 -33
  312. package/src/verblets/number-with-units/index.examples.js +0 -38
  313. package/src/verblets/number-with-units/index.js +0 -84
  314. package/src/verblets/number-with-units/index.spec.js +0 -46
  315. package/src/verblets/number-with-units/number-with-units-result.json +0 -23
  316. package/src/verblets/people-list/README.md +0 -28
  317. package/src/verblets/people-list/index.examples.js +0 -184
  318. package/src/verblets/people-list/index.js +0 -44
  319. package/src/verblets/people-list/index.spec.js +0 -49
  320. package/src/verblets/schema-org/index.examples.js +0 -51
  321. package/src/verblets/schema-org/index.js +0 -37
  322. package/src/verblets/schema-org/index.spec.js +0 -39
  323. package/src/verblets/sentiment/README.md +0 -10
  324. package/src/verblets/sentiment/index.examples.js +0 -20
  325. package/src/verblets/sentiment/index.js +0 -9
  326. package/src/verblets/sentiment/index.spec.js +0 -20
  327. package/src/verblets/to-object/README.md +0 -38
  328. package/src/verblets/to-object/index.examples.js +0 -29
  329. package/src/verblets/to-object/index.js +0 -131
  330. package/src/verblets/to-object/index.spec.js +0 -71
@@ -1,75 +0,0 @@
1
- import chatgpt from '../../lib/chatgpt/index.js';
2
-
3
- function buildEqualityPrompt({ actual, expected, context }) {
4
- return `Does the actual value strictly equal the expected value?\n\nActual: ${JSON.stringify(
5
- actual,
6
- null,
7
- 2
8
- )}\nExpected: ${JSON.stringify(expected, null, 2)}\n\n${
9
- context ? `Context: ${JSON.stringify(context, null, 2)}\n` : ''
10
- }Answer only "True" or "False".`;
11
- }
12
-
13
- function buildConstraintPrompt({ actual, constraint, context }) {
14
- return `Given this constraint: "${constraint}"\n\nActual value: ${JSON.stringify(
15
- actual,
16
- null,
17
- 2
18
- )}\n\n${
19
- context ? `Additional context: ${JSON.stringify(context, null, 2)}\n` : ''
20
- }Does the actual value satisfy the constraint? Answer only "True" or "False".`;
21
- }
22
-
23
- export async function llmAssert({
24
- actual,
25
- equals,
26
- constraint,
27
- context,
28
- throws = true,
29
- message,
30
- llm = {},
31
- }) {
32
- if (equals === undefined && !constraint)
33
- throw new TypeError('Provide either "equals" or "constraint".');
34
-
35
- const prompt =
36
- equals !== undefined
37
- ? buildEqualityPrompt({ actual, expected: equals, context })
38
- : buildConstraintPrompt({ actual, constraint, context });
39
-
40
- const answer = await chatgpt(prompt, { modelOptions: llm });
41
- const text = typeof answer === 'string' ? answer : answer.content;
42
- const passed = /^true$/i.test(text.trim());
43
-
44
- if (!passed && throws) {
45
- let msg;
46
- if (typeof message === 'function') {
47
- msg = message({ actual, equals, constraint });
48
- } else {
49
- msg = message;
50
- }
51
-
52
- if (!msg) {
53
- msg =
54
- equals !== undefined
55
- ? 'LLM assertion failed: Does the actual value strictly equal the expected value?'
56
- : `LLM assertion failed: ${constraint}`;
57
- }
58
-
59
- throw new Error(msg);
60
- }
61
-
62
- return passed;
63
- }
64
-
65
- export default function expect(actual, shared = {}) {
66
- const run = (payload, opts) => llmAssert({ actual, ...payload, ...shared, ...opts });
67
- return {
68
- toEqual(expected, opts) {
69
- return run({ equals: expected }, opts);
70
- },
71
- toSatisfy(constraint, opts) {
72
- return run({ constraint }, opts);
73
- },
74
- };
75
- }
@@ -1,127 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest';
2
- import aiExpect from './index.js';
3
- import { longTestTimeout } from '../../constants/common.js';
4
-
5
- // Mock the chatgpt function to avoid actual API calls
6
- vi.mock('../../lib/chatgpt/index.js', () => ({
7
- default: vi.fn().mockImplementation((prompt) => {
8
- // Handle exact equality checks
9
- if (prompt.includes('Does the actual value strictly equal the expected value?')) {
10
- if (prompt.includes('Actual: "hello"') && prompt.includes('Expected: "hello"')) {
11
- return 'True';
12
- }
13
- if (prompt.includes('Actual: "goodbye"') && prompt.includes('Expected: "hello"')) {
14
- return 'False';
15
- }
16
- }
17
-
18
- // Handle constraint-based validations (format: "Given this constraint:")
19
- if (prompt.includes('Given this constraint:')) {
20
- if (prompt.includes('Is this a greeting?') && prompt.includes('Hello world!')) {
21
- return 'True';
22
- }
23
-
24
- if (prompt.includes('Is this text professional and grammatically correct?')) {
25
- if (prompt.includes('well-written, professional email')) {
26
- return 'True';
27
- }
28
- }
29
-
30
- if (prompt.includes('Does this person data look realistic?')) {
31
- if (prompt.includes('John Doe') && prompt.includes('"age": 30')) {
32
- return 'True';
33
- }
34
- }
35
-
36
- if (prompt.includes('Is this recommendation specific and actionable?')) {
37
- if (prompt.includes('Increase marketing budget by 20%')) {
38
- return 'True';
39
- }
40
- }
41
- }
42
-
43
- // Default to False for unmatched cases
44
- return 'False';
45
- }),
46
- }));
47
-
48
- describe('expect verblet', () => {
49
- it(
50
- 'should pass for exact equality',
51
- async () => {
52
- const result = await aiExpect('hello').toEqual('hello', { throws: false });
53
- expect(result).toBe(true);
54
- },
55
- longTestTimeout
56
- );
57
-
58
- it(
59
- 'should pass for constraint-based validation',
60
- async () => {
61
- const result = await aiExpect('Hello world!').toSatisfy('Is this a greeting?', {
62
- throws: false,
63
- });
64
- expect(result).toBe(true);
65
- },
66
- longTestTimeout
67
- );
68
-
69
- it(
70
- 'should fail for non-matching values',
71
- async () => {
72
- const result = await aiExpect('goodbye').toEqual('hello', {
73
- throws: false,
74
- });
75
- expect(result).toBe(false);
76
- },
77
- longTestTimeout
78
- );
79
-
80
- it(
81
- 'should validate content quality',
82
- async () => {
83
- const result = await aiExpect(
84
- 'This is a well-written, professional email with proper grammar.'
85
- ).toSatisfy('Is this text professional and grammatically correct?', {
86
- throws: false,
87
- });
88
- expect(result).toBe(true);
89
- },
90
- longTestTimeout
91
- );
92
-
93
- it(
94
- 'should validate data structures',
95
- async () => {
96
- const result = await aiExpect({ name: 'John Doe', age: 30, city: 'New York' }).toSatisfy(
97
- 'Does this person data look realistic?',
98
- { throws: false }
99
- );
100
- expect(result).toBe(true);
101
- },
102
- longTestTimeout
103
- );
104
-
105
- it(
106
- 'should handle business logic validation',
107
- async () => {
108
- const result = await aiExpect(
109
- 'Increase marketing budget by 20% for Q4 to boost holiday sales'
110
- ).toSatisfy('Is this recommendation specific and actionable?', {
111
- throws: false,
112
- });
113
- expect(result).toBe(true);
114
- },
115
- longTestTimeout
116
- );
117
-
118
- it(
119
- 'should throw by default on failure',
120
- async () => {
121
- await expect(async () => {
122
- await aiExpect('hello').toEqual('goodbye');
123
- }).rejects.toThrow('LLM assertion failed');
124
- },
125
- longTestTimeout
126
- );
127
- });
@@ -1,139 +0,0 @@
1
- import Ajv from 'ajv';
2
- import fs from 'node:fs/promises';
3
- import { describe, expect, it, beforeAll, afterAll } from 'vitest';
4
- import { aiExpect } from '../../chains/expect/index.js';
5
- import { longTestTimeout } from '../../constants/common.js';
6
- import { fileURLToPath } from 'url';
7
- import { dirname, join } from 'path';
8
-
9
- import intent from './index.js';
10
-
11
- const __filename = fileURLToPath(import.meta.url);
12
- const __dirname = dirname(__filename);
13
-
14
- async function getIntentSchema() {
15
- return JSON.parse(await fs.readFile(join(__dirname, '../../json-schemas/intent.json')));
16
- }
17
-
18
- const examples = [
19
- {
20
- inputs: { text: 'Give me a flight to Burgas' },
21
- want: { resultSchema: getIntentSchema },
22
- },
23
- {
24
- inputs: {
25
- text: 'Lookup a song by the quote \
26
- "I just gotta tell you how I\'m feeling"',
27
- },
28
- want: { resultSchema: getIntentSchema },
29
- },
30
- ];
31
-
32
- describe('Intent verblet', () => {
33
- // Set environment mode to 'none' for all tests to avoid throwing
34
- const originalMode = process.env.LLM_EXPECT_MODE;
35
-
36
- beforeAll(() => {
37
- process.env.LLM_EXPECT_MODE = 'none';
38
- });
39
-
40
- afterAll(() => {
41
- if (originalMode !== undefined) {
42
- process.env.LLM_EXPECT_MODE = originalMode;
43
- } else {
44
- delete process.env.LLM_EXPECT_MODE;
45
- }
46
- });
47
-
48
- examples.forEach((example) => {
49
- it(
50
- example.inputs.text,
51
- async () => {
52
- const result = await intent({ text: example.inputs.text });
53
-
54
- if (example.want.resultSchema) {
55
- const schema = await example.want.resultSchema();
56
- const ajv = new Ajv();
57
- const validate = ajv.compile(schema);
58
-
59
- const isValid = validate(result);
60
- if (!isValid) {
61
- console.error('Validation errors:');
62
- console.error(validate.errors);
63
- console.error('Returned result:');
64
- console.error(JSON.stringify(result, null, 2));
65
- }
66
- expect(isValid).toStrictEqual(true);
67
-
68
- // LLM assertion to validate intent extraction quality
69
- const intentMakesSense = await aiExpect(
70
- `Original text: "${example.inputs.text}" was parsed into an intent object`
71
- ).toSatisfy('Does this seem like a reasonable intent extraction?');
72
- expect(intentMakesSense).toBe(true);
73
-
74
- // Additional assertion for intent completeness
75
- const hasBasicInfo = await aiExpect(JSON.stringify(result)).toSatisfy(
76
- 'Does this intent object contain some useful information?'
77
- );
78
- expect(hasBasicInfo).toBe(true);
79
- }
80
- },
81
- longTestTimeout
82
- );
83
- });
84
-
85
- it(
86
- 'should extract travel booking intent correctly',
87
- async () => {
88
- const travelRequest =
89
- 'Book me a round-trip flight from New York to Tokyo for next month, preferably business class';
90
- const result = await intent({ text: travelRequest });
91
-
92
- // Traditional schema validation
93
- const schema = await getIntentSchema();
94
- const ajv = new Ajv();
95
- const validate = ajv.compile(schema);
96
- expect(validate(result)).toBe(true);
97
-
98
- // LLM assertions for travel-specific validation
99
- const isTravelRelated = await aiExpect(`Intent extracted from: "${travelRequest}"`).toSatisfy(
100
- 'Is this request related to travel or transportation?'
101
- );
102
- expect(isTravelRelated).toBe(true);
103
-
104
- const hasLocationInfo = await aiExpect(JSON.stringify(result)).toSatisfy(
105
- 'Does this intent mention any locations or destinations?'
106
- );
107
- expect(hasLocationInfo).toBe(true);
108
- },
109
- longTestTimeout
110
- );
111
-
112
- it(
113
- 'should handle entertainment search intent',
114
- async () => {
115
- const musicQuery =
116
- 'Find that song that goes "Never gonna give you up, never gonna let you down"';
117
- const result = await intent({ text: musicQuery });
118
-
119
- // Schema validation
120
- const schema = await getIntentSchema();
121
- const ajv = new Ajv();
122
- const validate = ajv.compile(schema);
123
- expect(validate(result)).toBe(true);
124
-
125
- // LLM assertion for entertainment intent
126
- const isEntertainmentRelated = await aiExpect(
127
- `Intent extracted from: "${musicQuery}"`
128
- ).toSatisfy('Is this request related to music or entertainment?');
129
- expect(isEntertainmentRelated).toBe(true);
130
-
131
- // Validate that the intent captures the search criteria
132
- const mentionsLyrics = await aiExpect(JSON.stringify(result)).toSatisfy(
133
- 'Does this intent mention song lyrics or music search?'
134
- );
135
- expect(mentionsLyrics).toBe(true);
136
- },
137
- longTestTimeout
138
- );
139
- });
@@ -1,60 +0,0 @@
1
- import fs from 'node:fs/promises';
2
- import path from 'node:path';
3
- import { fileURLToPath } from 'node:url';
4
- import chatGPT from '../../lib/chatgpt/index.js';
5
- import { constants as promptConstants } from '../../prompts/index.js';
6
-
7
- const { contentIsQuestion } = promptConstants;
8
-
9
- // Get the directory of this module
10
- const __filename = fileURLToPath(import.meta.url);
11
- const __dirname = path.dirname(__filename);
12
-
13
- /**
14
- * Load the JSON schema for intent results
15
- * @returns {Promise<Object>} JSON schema for validation
16
- */
17
- async function getIntentSchema() {
18
- const schemaPath = path.resolve(__dirname, '../../json-schemas/intent.json');
19
- return JSON.parse(await fs.readFile(schemaPath, 'utf8'));
20
- }
21
-
22
- /**
23
- * Create model options for structured outputs
24
- * @param {string|Object} llm - LLM model name or configuration object
25
- * @returns {Promise<Object>} Model options for chatGPT
26
- */
27
- async function createModelOptions(llm = 'fastGoodCheap') {
28
- const schema = await getIntentSchema();
29
-
30
- const responseFormat = {
31
- type: 'json_schema',
32
- json_schema: {
33
- name: 'intent_result',
34
- schema,
35
- },
36
- };
37
-
38
- if (typeof llm === 'string') {
39
- return {
40
- modelName: llm,
41
- response_format: responseFormat,
42
- };
43
- } else {
44
- return {
45
- ...llm,
46
- response_format: responseFormat,
47
- };
48
- }
49
- }
50
-
51
- export default async function intent({ text, config = {} } = {}) {
52
- const { llm, ...options } = config;
53
- const prompt = `${contentIsQuestion} What is the intent of this text?\n\n${text}`;
54
-
55
- const modelOptions = await createModelOptions(llm);
56
- const response = await chatGPT(prompt, { modelOptions, ...options });
57
-
58
- // With structured outputs, response should already be parsed
59
- return typeof response === 'string' ? JSON.parse(response) : response;
60
- }
@@ -1,31 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest';
2
-
3
- import intent from './index.js';
4
-
5
- vi.mock('../../lib/chatgpt/index.js', () => ({
6
- default: vi.fn().mockImplementation((text) => {
7
- if (/a flight to/.test(text)) {
8
- return '{}';
9
- }
10
- return 'undefined';
11
- }),
12
- }));
13
-
14
- const examples = [
15
- {
16
- name: 'Basic usage',
17
- inputs: { text: 'Give me a flight to Burgas' },
18
- want: { typeOfResult: 'object' },
19
- },
20
- ];
21
-
22
- describe('Intent verblet', () => {
23
- examples.forEach((example) => {
24
- it(example.name, async () => {
25
- const result = await intent({ text: example.inputs.text });
26
- if (example.want.typeOfResult) {
27
- expect(typeof result).toStrictEqual(example.want.typeOfResult);
28
- }
29
- });
30
- });
31
- });
@@ -1,16 +0,0 @@
1
- # intersection
2
-
3
- Find common threads between multiple items using an LLM. The verblet checks every combination from pairs up to the full set. If no relationship is obvious, an empty array is returned.
4
-
5
- ```javascript
6
- import intersection from './index.js';
7
-
8
- await intersection(['smartphone', 'tablet', 'laptop']);
9
- // => ['Portable electronics', 'Portable computers']
10
-
11
- // Provide custom instructions for how to find intersections
12
- await intersection(['car', 'bicycle', 'train'], {
13
- instructions: 'focus on transportation methods available in a city',
14
- });
15
- // => ['Wheeled vehicles', 'Public transit']
16
- ```
@@ -1,89 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import intersection from './index.js';
3
- import { longTestTimeout } from '../../constants/common.js';
4
- import aiExpect from '../expect/index.js';
5
-
6
- describe('intersection examples', () => {
7
- it(
8
- 'finds commonalities among devices',
9
- async () => {
10
- const result = await intersection(['smartphone', 'laptop', 'tablet']);
11
- expect(Array.isArray(result), `Saw: ${JSON.stringify(result)}`).toBe(true);
12
-
13
- // LLM assertion to verify the intersection contains meaningful commonalities
14
- await aiExpect(result).toSatisfy(
15
- 'should be an array of strings that could reasonably represent commonalities between technology devices',
16
- {
17
- context: 'Testing intersection verblet with electronic devices',
18
- }
19
- );
20
- },
21
- longTestTimeout
22
- );
23
-
24
- it(
25
- 'finds commonalities among animals',
26
- async () => {
27
- const result = await intersection(['dog', 'cat', 'bird']);
28
- expect(Array.isArray(result), `Saw: ${JSON.stringify(result)}`).toBe(true);
29
-
30
- // LLM assertion for animal traits - be more lenient
31
- await aiExpect(result).toSatisfy(
32
- 'should be an array that represents some form of analysis or commonalities related to animals',
33
- {
34
- context: 'Testing intersection verblet with animals',
35
- }
36
- );
37
-
38
- // Just check that it's an array - don't require specific content
39
- expect(Array.isArray(result)).toBe(true);
40
- },
41
- longTestTimeout
42
- );
43
-
44
- it(
45
- 'handles abstract concepts',
46
- async () => {
47
- const result = await intersection(['love', 'friendship', 'trust']);
48
- expect(Array.isArray(result), `Saw: ${JSON.stringify(result)}`).toBe(true);
49
-
50
- // LLM assertion for abstract concept intersections - be more specific
51
- await aiExpect(result).toSatisfy(
52
- 'should be an array of strings representing common emotional or relational concepts that love, friendship, and trust share (like emotional connection, mutual respect, care, etc.)',
53
- {
54
- context: 'Testing intersection verblet with abstract concepts: love, friendship, trust',
55
- }
56
- );
57
-
58
- // Verify it's a non-empty array with string elements
59
- expect(Array.isArray(result)).toBe(true);
60
- expect(result.length).toBeGreaterThan(0);
61
- expect(result.every((item) => typeof item === 'string')).toBe(true);
62
- },
63
- longTestTimeout
64
- );
65
-
66
- it(
67
- 'works with single item',
68
- async () => {
69
- const result = await intersection(['bicycle']);
70
- expect(Array.isArray(result), `Saw: ${JSON.stringify(result)}`).toBe(true);
71
-
72
- // Single items should return empty array based on the implementation
73
- expect(result.length).toBe(0);
74
- },
75
- longTestTimeout
76
- );
77
-
78
- it(
79
- 'handles empty input gracefully',
80
- async () => {
81
- const result = await intersection([]);
82
- expect(Array.isArray(result), `Saw: ${JSON.stringify(result)}`).toBe(true);
83
-
84
- // Empty input should return empty array
85
- expect(result.length).toBe(0);
86
- },
87
- longTestTimeout
88
- );
89
- });
@@ -1,125 +0,0 @@
1
- import fs from 'node:fs/promises';
2
- import path from 'node:path';
3
- import { fileURLToPath } from 'node:url';
4
- import chatGPT from '../../lib/chatgpt/index.js';
5
- import wrapVariable from '../../prompts/wrap-variable.js';
6
- import { constants as promptConstants } from '../../prompts/index.js';
7
-
8
- const { contentIsQuestion, tryCompleteData, onlyJSONStringArray } = promptConstants;
9
-
10
- // Get the directory of this module
11
- const __filename = fileURLToPath(import.meta.url);
12
- const __dirname = path.dirname(__filename);
13
-
14
- /**
15
- * Load the JSON schema for intersection results
16
- * @returns {Promise<Object>} JSON schema for validation
17
- */
18
- async function getIntersectionSchema() {
19
- const schemaPath = path.join(__dirname, 'intersection-result.json');
20
- return JSON.parse(await fs.readFile(schemaPath, 'utf8'));
21
- }
22
-
23
- /**
24
- * Create model options for structured outputs
25
- * @param {string|Object} llm - LLM model name or configuration object
26
- * @returns {Promise<Object>} Model options for chatGPT
27
- */
28
- async function createModelOptions(llm = 'fastGoodCheap') {
29
- const schema = await getIntersectionSchema();
30
-
31
- const responseFormat = {
32
- type: 'json_schema',
33
- json_schema: {
34
- name: 'intersection_result',
35
- schema,
36
- },
37
- };
38
-
39
- if (typeof llm === 'string') {
40
- return {
41
- modelName: llm,
42
- response_format: responseFormat,
43
- };
44
- } else {
45
- return {
46
- ...llm,
47
- response_format: responseFormat,
48
- };
49
- }
50
- }
51
-
52
- export const buildPrompt = (items, { instructions } = {}) => {
53
- const itemsList = items.join(' | ');
54
- const itemsBlock = wrapVariable(itemsList, { tag: 'items' });
55
- const intro =
56
- instructions ||
57
- 'Identify the common elements, shared features, or overlapping aspects that connect all the given items.';
58
-
59
- return `${contentIsQuestion} ${intro}
60
-
61
- ${itemsBlock}
62
-
63
- Provide a clear, focused list of items that represent the intersection or commonality between all the given categories.
64
-
65
- ${tryCompleteData} ${onlyJSONStringArray}`;
66
- };
67
-
68
- export default async function intersection(items, config = {}) {
69
- if (!Array.isArray(items) || items.length === 0) {
70
- return [];
71
- }
72
-
73
- // Intersection requires at least 2 items
74
- if (items.length < 2) {
75
- return [];
76
- }
77
-
78
- const { llm, ...options } = config;
79
- const modelOptions = await createModelOptions(llm);
80
-
81
- const output = await chatGPT(buildPrompt(items, options), {
82
- modelOptions,
83
- });
84
-
85
- // Handle JSON parsing with robust error handling
86
- let parsed;
87
- try {
88
- if (typeof output === 'string') {
89
- // Try to clean up common JSON issues
90
- let cleanedOutput = output.trim();
91
-
92
- // Remove any text before the first { or [
93
- const jsonStart = Math.min(
94
- cleanedOutput.indexOf('{') !== -1 ? cleanedOutput.indexOf('{') : Infinity,
95
- cleanedOutput.indexOf('[') !== -1 ? cleanedOutput.indexOf('[') : Infinity
96
- );
97
-
98
- if (jsonStart !== Infinity && jsonStart > 0) {
99
- cleanedOutput = cleanedOutput.substring(jsonStart);
100
- }
101
-
102
- // Remove any text after the last } or ]
103
- const lastBrace = cleanedOutput.lastIndexOf('}');
104
- const lastBracket = cleanedOutput.lastIndexOf(']');
105
- const jsonEnd = Math.max(lastBrace, lastBracket);
106
-
107
- if (jsonEnd !== -1 && jsonEnd < cleanedOutput.length - 1) {
108
- cleanedOutput = cleanedOutput.substring(0, jsonEnd + 1);
109
- }
110
-
111
- parsed = JSON.parse(cleanedOutput);
112
- } else {
113
- parsed = output;
114
- }
115
- } catch (error) {
116
- console.error('Failed to parse JSON response from LLM:', error.message);
117
- console.error('Raw output:', output);
118
-
119
- return [];
120
- }
121
-
122
- // Extract the items array from the object structure
123
- const resultArray = parsed?.items || parsed;
124
- return Array.isArray(resultArray) ? resultArray.filter(Boolean) : [];
125
- }