@far-world-labs/verblets 0.1.1

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 (167) hide show
  1. package/.eslintrc.json +42 -0
  2. package/.husky/pre-commit +4 -0
  3. package/.release-it.json +9 -0
  4. package/.vite.config.examples.js +8 -0
  5. package/.vite.config.js +8 -0
  6. package/docker-compose.yml +7 -0
  7. package/docs/README.md +41 -0
  8. package/docs/babel.config.js +3 -0
  9. package/docs/blog/2019-05-28-first-blog-post.md +12 -0
  10. package/docs/blog/2019-05-29-long-blog-post.md +44 -0
  11. package/docs/blog/2021-08-01-mdx-blog-post.mdx +20 -0
  12. package/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg +0 -0
  13. package/docs/blog/2021-08-26-welcome/index.md +25 -0
  14. package/docs/blog/authors.yml +17 -0
  15. package/docs/docs/api/bool.md +74 -0
  16. package/docs/docs/api/search.md +51 -0
  17. package/docs/docs/intro.md +47 -0
  18. package/docs/docs/tutorial-basics/_category_.json +8 -0
  19. package/docs/docs/tutorial-basics/congratulations.md +23 -0
  20. package/docs/docs/tutorial-basics/create-a-blog-post.md +34 -0
  21. package/docs/docs/tutorial-basics/create-a-document.md +57 -0
  22. package/docs/docs/tutorial-basics/create-a-page.md +43 -0
  23. package/docs/docs/tutorial-basics/deploy-your-site.md +31 -0
  24. package/docs/docs/tutorial-basics/markdown-features.mdx +152 -0
  25. package/docs/docs/tutorial-extras/_category_.json +7 -0
  26. package/docs/docs/tutorial-extras/img/docsVersionDropdown.png +0 -0
  27. package/docs/docs/tutorial-extras/img/localeDropdown.png +0 -0
  28. package/docs/docs/tutorial-extras/manage-docs-versions.md +55 -0
  29. package/docs/docs/tutorial-extras/translate-your-site.md +88 -0
  30. package/docs/docusaurus.config.js +120 -0
  31. package/docs/package.json +44 -0
  32. package/docs/sidebars.js +31 -0
  33. package/docs/src/components/HomepageFeatures/index.js +61 -0
  34. package/docs/src/components/HomepageFeatures/styles.module.css +11 -0
  35. package/docs/src/css/custom.css +30 -0
  36. package/docs/src/pages/index.js +43 -0
  37. package/docs/src/pages/index.module.css +23 -0
  38. package/docs/src/pages/markdown-page.md +7 -0
  39. package/docs/static/.nojekyll +0 -0
  40. package/docs/static/img/docusaurus-social-card.jpg +0 -0
  41. package/docs/static/img/docusaurus.png +0 -0
  42. package/docs/static/img/favicon.ico +0 -0
  43. package/docs/static/img/logo.svg +1 -0
  44. package/docs/static/img/undraw_docusaurus_mountain.svg +171 -0
  45. package/docs/static/img/undraw_docusaurus_react.svg +170 -0
  46. package/docs/static/img/undraw_docusaurus_tree.svg +40 -0
  47. package/package.json +75 -0
  48. package/scripts/generate-chain/index.js +111 -0
  49. package/scripts/generate-lib/index.js +68 -0
  50. package/scripts/generate-test/index.js +111 -0
  51. package/scripts/generate-verblet/README.md +17 -0
  52. package/scripts/generate-verblet/index.js +110 -0
  53. package/scripts/run.sh +15 -0
  54. package/scripts/runner/index.js +30 -0
  55. package/scripts/simple-editor/README.md +34 -0
  56. package/scripts/simple-editor/index.js +68 -0
  57. package/scripts/summarize-files/index.js +46 -0
  58. package/src/chains/dismantle/dismantle.examples.js +0 -0
  59. package/src/chains/dismantle/index.examples.js +30 -0
  60. package/src/chains/dismantle/index.js +314 -0
  61. package/src/chains/dismantle/index.spec.js +33 -0
  62. package/src/chains/list/index.examples.js +72 -0
  63. package/src/chains/list/index.js +161 -0
  64. package/src/chains/list/index.spec.js +68 -0
  65. package/src/chains/list/schema.json +24 -0
  66. package/src/chains/questions/index.examples.js +68 -0
  67. package/src/chains/questions/index.js +136 -0
  68. package/src/chains/questions/index.spec.js +29 -0
  69. package/src/chains/scan-js/index.js +119 -0
  70. package/src/chains/sort/index.examples.js +40 -0
  71. package/src/chains/sort/index.js +113 -0
  72. package/src/chains/sort/index.spec.js +115 -0
  73. package/src/chains/summary-map/README.md +33 -0
  74. package/src/chains/summary-map/index.examples.js +57 -0
  75. package/src/chains/summary-map/index.js +208 -0
  76. package/src/chains/summary-map/index.spec.js +78 -0
  77. package/src/chains/test/index.js +118 -0
  78. package/src/chains/test-advice/index.js +36 -0
  79. package/src/constants/common.js +9 -0
  80. package/src/constants/messages.js +3 -0
  81. package/src/constants/openai.js +65 -0
  82. package/src/index.js +33 -0
  83. package/src/json-schemas/cars-test.json +11 -0
  84. package/src/json-schemas/index.js +18 -0
  85. package/src/json-schemas/intent.json +38 -0
  86. package/src/json-schemas/schema-dot-org-photograph.json +127 -0
  87. package/src/json-schemas/schema-dot-org-place.json +56 -0
  88. package/src/lib/any-signal/index.js +28 -0
  89. package/src/lib/chatgpt/index.js +143 -0
  90. package/src/lib/editor/index.js +31 -0
  91. package/src/lib/parse-js-parts/index.js +333 -0
  92. package/src/lib/parse-js-parts/index.spec.js +156 -0
  93. package/src/lib/path-aliases/index.js +39 -0
  94. package/src/lib/path-aliases/index.spec.js +70 -0
  95. package/src/lib/pave/index.js +34 -0
  96. package/src/lib/pave/index.spec.js +73 -0
  97. package/src/lib/prompt-cache/index.js +46 -0
  98. package/src/lib/retry/index.js +63 -0
  99. package/src/lib/retry/index.spec.js +86 -0
  100. package/src/lib/search-best-first/index.js +66 -0
  101. package/src/lib/search-js-files/code-features-property-definitions.json +123 -0
  102. package/src/lib/search-js-files/index.examples.js +22 -0
  103. package/src/lib/search-js-files/index.js +158 -0
  104. package/src/lib/search-js-files/index.spec.js +34 -0
  105. package/src/lib/search-js-files/scan-file.js +253 -0
  106. package/src/lib/shorten-text/index.js +30 -0
  107. package/src/lib/shorten-text/index.spec.js +68 -0
  108. package/src/lib/strip-numeric/index.js +5 -0
  109. package/src/lib/strip-response/index.js +35 -0
  110. package/src/lib/timed-abort-controller/index.js +41 -0
  111. package/src/lib/to-bool/index.js +8 -0
  112. package/src/lib/to-enum/index.js +14 -0
  113. package/src/lib/to-number/index.js +12 -0
  114. package/src/lib/to-number-with-units/index.js +51 -0
  115. package/src/lib/transcribe/index.js +61 -0
  116. package/src/prompts/README.md +15 -0
  117. package/src/prompts/as-enum.js +5 -0
  118. package/src/prompts/as-json-schema.js +9 -0
  119. package/src/prompts/as-object-with-schema.js +31 -0
  120. package/src/prompts/as-schema-org-text.js +17 -0
  121. package/src/prompts/as-schema-org-type.js +1 -0
  122. package/src/prompts/blog-post.js +7 -0
  123. package/src/prompts/code-features.js +28 -0
  124. package/src/prompts/constants.js +101 -0
  125. package/src/prompts/features-json-schema.js +27 -0
  126. package/src/prompts/generate-collection.js +26 -0
  127. package/src/prompts/generate-list.js +48 -0
  128. package/src/prompts/generate-questions.js +19 -0
  129. package/src/prompts/index.js +20 -0
  130. package/src/prompts/intent.js +66 -0
  131. package/src/prompts/output-succinct-names.js +3 -0
  132. package/src/prompts/select-from-threshold.js +18 -0
  133. package/src/prompts/sort.js +35 -0
  134. package/src/prompts/style.js +41 -0
  135. package/src/prompts/summarize.js +13 -0
  136. package/src/prompts/token-budget.js +3 -0
  137. package/src/prompts/wrap-list.js +14 -0
  138. package/src/prompts/wrap-variable.js +36 -0
  139. package/src/services/llm-model/index.js +114 -0
  140. package/src/services/llm-model/model.js +21 -0
  141. package/src/services/redis/index.js +84 -0
  142. package/src/verblets/auto/index.examples.js +28 -0
  143. package/src/verblets/auto/index.js +28 -0
  144. package/src/verblets/auto/index.spec.js +34 -0
  145. package/src/verblets/bool/index.examples.js +28 -0
  146. package/src/verblets/bool/index.js +28 -0
  147. package/src/verblets/bool/index.schema.json +14 -0
  148. package/src/verblets/bool/index.spec.js +35 -0
  149. package/src/verblets/enum/index.examples.js +33 -0
  150. package/src/verblets/enum/index.js +15 -0
  151. package/src/verblets/enum/index.spec.js +35 -0
  152. package/src/verblets/intent/index.examples.js +51 -0
  153. package/src/verblets/intent/index.js +72 -0
  154. package/src/verblets/intent/index.spec.js +31 -0
  155. package/src/verblets/number/index.examples.js +33 -0
  156. package/src/verblets/number/index.js +22 -0
  157. package/src/verblets/number/index.spec.js +35 -0
  158. package/src/verblets/number-with-units/index.examples.js +34 -0
  159. package/src/verblets/number-with-units/index.js +19 -0
  160. package/src/verblets/number-with-units/index.spec.js +46 -0
  161. package/src/verblets/schema-org/index.examples.js +56 -0
  162. package/src/verblets/schema-org/index.js +8 -0
  163. package/src/verblets/schema-org/index.spec.js +39 -0
  164. package/src/verblets/to-object/README.md +38 -0
  165. package/src/verblets/to-object/index.examples.js +29 -0
  166. package/src/verblets/to-object/index.js +136 -0
  167. package/src/verblets/to-object/index.spec.js +74 -0
@@ -0,0 +1,57 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import SummaryMap from './index.js';
4
+ import { longTestTimeout } from '../../constants/common.js';
5
+ import pave from '../../lib/pave/index.js';
6
+
7
+ const legalText = `Pursuant to the stipulations delineated herein, the parties hereto, designated as Party A (the "Grantor") and Party B (the "Grantee"), do hereby irrevocably and unconditionally covenant to abide by the complex and intricate provisions associated with the lesser-known subject matter of usufructuary rights in the realm of riparian watercourses, specifically encompassing the doctrine of correlative rights and the principle of reasonable use, in accordance with the heretofore undisclosed specifications set forth in Schedule U-1.
8
+
9
+ In pursuance of the aforesaid stipulations, the Grantee shall obtain a non-exclusive, non-transferable, revocable license, subject to the limitations and conditions imposed by the pertinent jurisdictional authorities, to make equitable utilization of the watercourse, taking into account the natural flow, hydrological cycle, and ecological balance, while ensuring the avoidance of significant harm to other riparians by employing the best practicable means in conformity with the established standards of reasonableness, as delineated in the annexed Exhibit R-1, which sets forth the methodology for the determination of the equitable apportionment of said watercourse, and the allocation of the respective usufructuary rights.`;
10
+
11
+ const codeText = `
12
+ function _generateKey(seed, length) {
13
+ let key = '';
14
+ let curr = seed;
15
+ for (let i = 0; i < length; i++) {
16
+ curr = (1664525 * curr + 1013904223) % 4294967296;
17
+ key += String.fromCharCode(curr % 256);
18
+ }
19
+ return key;
20
+ }
21
+
22
+ function _xorStrings(a, b) {
23
+ let res = '';
24
+ for (let i = 0; i < a.length; i++) {
25
+ res += String.fromCharCode(a.charCodeAt(i) ^ b.charCodeAt(i));
26
+ }
27
+ return res;
28
+ }
29
+
30
+ function encodeDecode(input, seed) {
31
+ let key = _generateKey(seed, input.length);
32
+ return _xorStrings(input, key);
33
+ }
34
+ `;
35
+
36
+ describe('Summary map', () => {
37
+ it(
38
+ 'Example',
39
+ async () => {
40
+ const map = new SummaryMap({ targetTokens: 600 });
41
+
42
+ map.set('a.b.c', { value: legalText, weight: 0.01 });
43
+ map.set('a.d', { value: codeText, type: 'code', weight: 0.7 });
44
+ map.set('e.0', { value: 'abc', weight: 0.01 });
45
+ map.set('e.3', {
46
+ value: 'The quick brown fox jump over the lazy dog',
47
+ weight: 0.7,
48
+ });
49
+
50
+ const entries = Array.from(await map.entries());
51
+ const result = entries.reduce((acc, [k, v]) => pave(acc, k, v), {});
52
+
53
+ expect(result).toBe(result);
54
+ },
55
+ longTestTimeout
56
+ );
57
+ });
@@ -0,0 +1,208 @@
1
+ /* eslint-disable no-await-in-loop */
2
+
3
+ import chatGPT from '../../lib/chatgpt/index.js';
4
+ import pave from '../../lib/pave/index.js';
5
+ import shortenText from '../../lib/shorten-text/index.js';
6
+ import {
7
+ summarize as basicSummarize,
8
+ tokenBudget,
9
+ } from '../../prompts/index.js';
10
+ import modelService from '../../services/llm-model/index.js';
11
+
12
+ const summarize = ({ budget, type, value, fixes = [] }) => {
13
+ if (budget) {
14
+ fixes.push(tokenBudget(budget));
15
+ }
16
+
17
+ if (type === 'code') {
18
+ fixes.push('Output function signature lines and a closing bracket.');
19
+ fixes.push(
20
+ 'Comment out the bodies of the functions and leave a summary of the implementation.'
21
+ );
22
+ fixes.push('Remove the function header if it exists.');
23
+ }
24
+
25
+ const fixesAsBullets = fixes.map((fix) => ` - ${fix}`);
26
+
27
+ return chatGPT(basicSummarize(value, `${fixesAsBullets.join('\n')}`));
28
+ };
29
+
30
+ /**
31
+ * SummaryMap is a utility class for automatically summarizing prompt inputs
32
+ * to fit within a desired desired token budget.
33
+ */
34
+ export default class SummaryMap extends Map {
35
+ constructor({
36
+ maxTokensPerValue,
37
+ model = modelService.getBestAvailableModel(),
38
+ promptText,
39
+ targetTokens,
40
+ // used with promptText, when targetTokens isn't supplied
41
+ targetTokensTotalRatio = 0.3,
42
+ }) {
43
+ super();
44
+ this.cache = new Map();
45
+ this.data = new Map();
46
+ this.isCacheValid = false;
47
+ this.maxTokensPerValue = maxTokensPerValue ?? model.maxTokens;
48
+
49
+ if (targetTokens) {
50
+ this.targetTokens = targetTokens;
51
+ } else if (promptText && model) {
52
+ this.promptTokens = model.toTokens(promptText).length;
53
+ const maxModelTokens = model.maxTokens;
54
+ const remainingTokens = maxModelTokens - this.promptTokens;
55
+ this.targetTokens = Math.floor(
56
+ remainingTokens - remainingTokens * targetTokensTotalRatio
57
+ );
58
+ } else {
59
+ throw new Error(
60
+ 'Either "promptText" and "model" or "targetTokens" must be provided.'
61
+ );
62
+ }
63
+ }
64
+
65
+ calculateBudgets() {
66
+ const totalSizeWeight = [...this.data.values()]
67
+ .filter((obj) => obj?.summary !== false)
68
+ .reduce((sum, valueObject) => {
69
+ return sum + (valueObject.weight ?? 1) * valueObject.value.length;
70
+ }, 0);
71
+ const sortedEntries = [...this.data.entries()].sort(
72
+ (a, b) => a[1].weight - b[1].weight
73
+ );
74
+
75
+ const budgets = [];
76
+ for (const [entryKey, valueObject] of sortedEntries) {
77
+ const sizeWeight = valueObject.value.length * (valueObject.weight ?? 1);
78
+ const budget = Math.floor(
79
+ (sizeWeight / totalSizeWeight) * this.targetTokens
80
+ );
81
+
82
+ if (valueObject.weight) {
83
+ budgets.push({ key: entryKey, budget });
84
+ } else {
85
+ budgets.push({ key: entryKey });
86
+ }
87
+ }
88
+
89
+ return { totalSizeWeight, budgets };
90
+ }
91
+
92
+ async myFillCache() {
93
+ const { budgets } = this.calculateBudgets();
94
+
95
+ for (const { key, budget } of budgets) {
96
+ const valueObject = this.data.get(key);
97
+
98
+ const value = shortenText(valueObject.value, {
99
+ targetTokenCount: this.maxTokensPerValue,
100
+ });
101
+
102
+ // omit weight to skip summarization
103
+ let summarizedValue = value;
104
+ if (budget) {
105
+ summarizedValue = await summarize({
106
+ budget,
107
+ fixes: valueObject.fixes,
108
+ type: valueObject.type,
109
+ value,
110
+ });
111
+ }
112
+
113
+ this.cache.set(key, summarizedValue);
114
+ }
115
+
116
+ this.isCacheValid = true;
117
+ }
118
+
119
+ getCache() {
120
+ return this.cache;
121
+ }
122
+
123
+ set(key, config) {
124
+ this.data.set(key, config);
125
+ this.cache.delete(key);
126
+ this.isCacheValid = false;
127
+ }
128
+
129
+ delete(key) {
130
+ this.data.delete(key);
131
+ this.cache.delete(key);
132
+ this.isCacheValid = false;
133
+ }
134
+
135
+ clear() {
136
+ this.data.clear();
137
+ this.cache.clear();
138
+ this.isCacheValid = false;
139
+ }
140
+
141
+ getStale(key) {
142
+ return this.cache.get(key);
143
+ }
144
+
145
+ get(key) {
146
+ if (!super.has(key)) {
147
+ return null;
148
+ }
149
+
150
+ if (!this.isCacheValid) {
151
+ return this.myFillCache()
152
+ .then(() => this.cache.get(key))
153
+ .catch((error) => {
154
+ return Promise.reject(error);
155
+ });
156
+ }
157
+
158
+ return Promise.resolve(this.getStale(key));
159
+ }
160
+
161
+ valuesStale() {
162
+ return this.cache.values();
163
+ }
164
+
165
+ values() {
166
+ if (!this.isCacheValid) {
167
+ return this.myFillCache()
168
+ .then(() => this.cache.values())
169
+ .catch((error) => {
170
+ return Promise.reject(error);
171
+ });
172
+ }
173
+ return Promise.resolve(this.valuesStale());
174
+ }
175
+
176
+ entriesStale() {
177
+ return this.cache.entries();
178
+ }
179
+
180
+ entries() {
181
+ if (!this.isCacheValid) {
182
+ return this.myFillCache()
183
+ .then(() => this.cache.entries())
184
+ .catch((error) => {
185
+ return Promise.reject(error);
186
+ });
187
+ }
188
+ return Promise.resolve(this.entriesStale());
189
+ }
190
+
191
+ pavedSummaryResultStale() {
192
+ return Array.from(this.entriesStale()).reduce(
193
+ (acc, [k, v]) => pave(acc, k, v),
194
+ {}
195
+ );
196
+ }
197
+
198
+ pavedSummaryResult() {
199
+ if (!this.isCacheValid) {
200
+ return this.myFillCache()
201
+ .then(() => this.pavedSummaryResultStale())
202
+ .catch((error) => {
203
+ return Promise.reject(error);
204
+ });
205
+ }
206
+ return Promise.resolve(this.pavedSummaryResultStale());
207
+ }
208
+ }
@@ -0,0 +1,78 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+
3
+ import SummaryMap from './index.js';
4
+ import pave from '../../lib/pave/index.js';
5
+
6
+ vi.mock('../../lib/chatgpt/index.js', () => ({
7
+ default: vi.fn().mockImplementation((text) => {
8
+ if (/Pursuant to the adjudication/.test(text)) {
9
+ return '01234567890123456789012345678901234567890123456789';
10
+ }
11
+ if (/rabin_karp_search/.test(text)) {
12
+ return '0123456789012345678901234';
13
+ }
14
+ return 'undefined';
15
+ }),
16
+ }));
17
+
18
+ const legalText = `Pursuant to the adjudication of a force majeure clause within the context of contractual`;
19
+
20
+ const codeText = `import numpy as np
21
+
22
+ def rabin_karp_search(pattern, text, prime=101):
23
+ `;
24
+
25
+ const examples = [
26
+ {
27
+ name: 'Basic usage',
28
+ inputs: {
29
+ targetTokens: 100,
30
+ keys: [
31
+ { key: 'example.text', value: legalText, weight: 1, type: 'text' },
32
+ { key: 'example.code', value: codeText, weight: 0.5, type: 'code' },
33
+ ],
34
+ },
35
+ wants: [
36
+ { key: 'example.text', resultLength: 50, budget: [60, 80] },
37
+ { key: 'example.code', resultLength: 25, budget: [20, 40] },
38
+ ],
39
+ },
40
+ ];
41
+
42
+ describe('Summary map', () => {
43
+ examples.forEach((example) => {
44
+ it(example.name, async () => {
45
+ const map = new SummaryMap({
46
+ targetTokens: example.inputs.targetTokens,
47
+ });
48
+
49
+ for (const input of example.inputs.keys) {
50
+ map.set(input.key, input);
51
+ }
52
+
53
+ const entries = Array.from(await map.entries());
54
+ const result = entries.reduce((acc, [k, v]) => pave(acc, k, v), {});
55
+
56
+ for (const want of example.wants) {
57
+ let value = result;
58
+
59
+ // Navigate the result object using the key segments
60
+ for (const keySegment of want.key.split('.')) {
61
+ value = value[keySegment];
62
+ }
63
+
64
+ expect(typeof value).toBe('string');
65
+
66
+ // Check if the length of the value is within the expected range
67
+ expect(value.length).toBeLessThanOrEqual(want.resultLength);
68
+
69
+ if (want.budget) {
70
+ const { budgets } = map.calculateBudgets();
71
+ const found = budgets.find((b) => b.key === want.key);
72
+ expect(found.budget).gt(want.budget[0]);
73
+ expect(found.budget).lt(want.budget[1]);
74
+ }
75
+ }
76
+ });
77
+ });
78
+ });
@@ -0,0 +1,118 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+
4
+ import { errorRunningTests } from '../../constants/messages.js';
5
+ import chatGPT from '../../lib/chatgpt/index.js';
6
+ import {
7
+ constants as promptConstants,
8
+ wrapVariable,
9
+ } from '../../prompts/index.js';
10
+ import modelService from '../../services/llm-model/index.js';
11
+ import toObject from '../../verblets/to-object/index.js';
12
+
13
+ const {
14
+ contentIsExample,
15
+ contentIsInstructions,
16
+ noFalseInformation,
17
+ onlyJSONArray,
18
+ onlyJSONStringArray,
19
+ useLineNumber,
20
+ } = promptConstants;
21
+
22
+ const contentIsChecksExamined =
23
+ 'These items were checked in an examination of the text:';
24
+ const contentIsExamined = 'The text examined:';
25
+ const findCodeImprovements =
26
+ 'Find specific improvements in the following code, not nitpicks.';
27
+ const gatherAsTestJSON =
28
+ 'Gather these discovered issues into a JSON format my tests module can consume.';
29
+
30
+ const testExamplesJSON = `[
31
+ {
32
+ name: '<copied from the supplied checks>',
33
+ expected: '<what you expected to see, your rationale for the change, give suggestions here, abbreviate to < 100 characters>',
34
+ saw: '<what you saw, being specific about where you see it, abbreviate to < 100 characters>',
35
+ isSuccess: false,
36
+ },
37
+ {
38
+ name: '<copied from the supplied checks>',
39
+ expected: '<what you expected to see, your rationale for the change, give suggestions here, abbreviate to < 100 characters>',
40
+ saw: '<what you saw, being specific about where you see it, abbreviate to < 100 characters>',
41
+ isSuccess: true,
42
+ },
43
+ <many more>
44
+ ]`;
45
+
46
+ const checksPrompt = (text, instructions) => `
47
+ ${contentIsInstructions} ${wrapVariable(instructions)}
48
+
49
+ ${wrapVariable(text, { tag: 'main-content' })}
50
+
51
+ ${useLineNumber}
52
+ ${noFalseInformation}
53
+
54
+ ${onlyJSONStringArray}
55
+ `;
56
+
57
+ const testsPrompt = (text, instructions, checks) => `${onlyJSONArray}
58
+
59
+ ${gatherAsTestJSON}
60
+
61
+ ${contentIsChecksExamined} ${wrapVariable(checks)}
62
+
63
+ ${contentIsExamined} ${wrapVariable(text, { tag: 'text-examined' })}
64
+
65
+ ${contentIsExample} ${wrapVariable(testExamplesJSON, { tag: 'example' })}
66
+
67
+ ${onlyJSONArray}
68
+ `;
69
+
70
+ export default async (
71
+ filePath,
72
+ instructions = findCodeImprovements,
73
+ model = modelService.getBestAvailableModel()
74
+ ) => {
75
+ const enableRegex = new RegExp(process.env.ENABLE_AI_TESTS ?? '^$');
76
+ if (!enableRegex.test(filePath)) {
77
+ return [];
78
+ }
79
+
80
+ try {
81
+ const filePathAbsolute = path.resolve(filePath);
82
+ const text = await fs.readFile(filePathAbsolute, 'utf-8');
83
+
84
+ const checksPromptCreated = checksPrompt(text, instructions);
85
+ const checksBudget = model.budgetTokens(checksPromptCreated);
86
+
87
+ const checksResult = await chatGPT(checksPromptCreated, {
88
+ modelOptions: {
89
+ maxTokens: checksBudget.completion,
90
+ },
91
+ });
92
+
93
+ const testsPromptCreated = testsPrompt(text, instructions, checksResult);
94
+ const testsBudget = model.budgetTokens(testsPromptCreated);
95
+
96
+ const results = await toObject(
97
+ await chatGPT(testsPromptCreated, {
98
+ modelOptions: {
99
+ maxTokens: testsBudget.completion,
100
+ },
101
+ })
102
+ );
103
+
104
+ if (!results.length) {
105
+ return [];
106
+ }
107
+
108
+ return results;
109
+ } catch (error) {
110
+ return [
111
+ {
112
+ name: errorRunningTests,
113
+ expected: 'tests generated',
114
+ saw: error.message,
115
+ },
116
+ ];
117
+ }
118
+ };
@@ -0,0 +1,36 @@
1
+ import test from '../test/index.js';
2
+
3
+ const boundaryIssues =
4
+ 'Run the code with 5 boundary value test cases and report any that fail';
5
+
6
+ const successIssues =
7
+ 'Identify 5 passing scenarios and significant boundary conditions in this code. Provide minimal input examples for each scenario to demonstrate correctness.';
8
+
9
+ const failureIssues = `Identify 5 failing scenarios and significant boundary conditions in this code. Provide minimal input examples for each scenario to demonstrate the failure. Assume DBC, and don't complain when types are specified in jsDoc.`;
10
+
11
+ const defectIssues =
12
+ 'Identify 5 defects in this code. Provide minimal input examples to demonstrate each defect.';
13
+
14
+ const bestPracticesIssues =
15
+ 'Suggest 5 best practices improvements for this code.';
16
+
17
+ const cleanCodeIssues = 'Suggest 5 "clean code" improvements for this code.';
18
+
19
+ const qualityIssues =
20
+ 'Identify 5 specific issues related to code quality, readability, and maintainability.';
21
+
22
+ const refactorIssues =
23
+ 'Suggest 5 refactors that would most improve the composibility of this code.';
24
+
25
+ export default async (path) => {
26
+ return [
27
+ ...(await test(path, boundaryIssues)),
28
+ ...(await test(path, successIssues)),
29
+ ...(await test(path, failureIssues)),
30
+ ...(await test(path, defectIssues)),
31
+ ...(await test(path, bestPracticesIssues)),
32
+ ...(await test(path, cleanCodeIssues)),
33
+ ...(await test(path, qualityIssues)),
34
+ ...(await test(path, refactorIssues)),
35
+ ];
36
+ };
@@ -0,0 +1,9 @@
1
+ /* eslint-disable import/prefer-default-export */
2
+
3
+ export const longTestTimeout = 120000;
4
+
5
+ export const maxRetries = 3;
6
+
7
+ export const retryDelay = 1000;
8
+
9
+ export const debugToObject = process.env.DEBUG_TO_OBJECT ?? false;
@@ -0,0 +1,3 @@
1
+ export const retryJSONParse =
2
+ 'The JSON parsing module will attempt recovery via internal retry. This message only appears in dev.';
3
+ export const errorRunningTests = 'Error running AI tests';
@@ -0,0 +1,65 @@
1
+ /* eslint-disable no-unused-expressions, import/prefer-default-export */
2
+
3
+ // Importing dotenv config to load environment variables from .env file
4
+ // eslint-disable-next-line no-unused-vars
5
+ import dotenv from 'dotenv/config';
6
+
7
+ import chai from 'chai';
8
+
9
+ const { expect } = chai;
10
+
11
+ // eslint-disable-next-line no-underscore-dangle
12
+ const _models = {
13
+ gpt35Turbo: {
14
+ endpoint: 'v1/chat/completions',
15
+ name: 'gpt-3.5-turbo-1106',
16
+ maxTokens: 16386,
17
+ requestTimeout: 40000,
18
+ },
19
+ textDavinci003: {
20
+ endpoint: 'v1/completions',
21
+ name: 'text-davinci-003',
22
+ maxTokens: 4097,
23
+ requestTimeout: 15000,
24
+ },
25
+ };
26
+
27
+ if (process.env.CHATGPT_V4_ENABLED) {
28
+ _models.gpt4 = {
29
+ endpoint: 'v1/chat/completions',
30
+ name: 'gpt-4-0125-preview',
31
+ maxTokens: 128000,
32
+ requestTimeout: 50000,
33
+ };
34
+ }
35
+
36
+ expect(process.env.OPENAI_API_KEY).to.exist;
37
+
38
+ export const apiKey = process.env.OPENAI_API_KEY;
39
+
40
+ export const apiUrl = 'https://api.openai.com/';
41
+
42
+ const secondsInDay = 60 * 60 * 24;
43
+ export const cacheTTL = process.env.CHATGPT_CACHE_TTL ?? secondsInDay;
44
+
45
+ export const debugPromptGlobally = process.env.CHATGPT_DEBUG_REQUEST ?? false;
46
+
47
+ export const debugPromptGloballyIfChanged =
48
+ process.env.CHATGPT_DEBUG_REQUEST_IF_CHANGED ?? false;
49
+
50
+ export const debugResultGlobally = process.env.CHATGPT_DEBUG_RESPONSE ?? false;
51
+
52
+ export const debugResultGloballyIfChanged =
53
+ process.env.CHATGPT_DEBUG_RESPONSE_IF_CHANGED ?? false;
54
+
55
+ export const frequencyPenalty = process.env.CHATGPT_FREQUENCY_PENALTY ?? 0;
56
+
57
+ export const models = _models;
58
+
59
+ export const operationTimeoutMultiplier = 2;
60
+
61
+ export const presencePenalty = process.env.CHATGPT_PRESENCE_PENALTY ?? 0;
62
+
63
+ export const temperature = process.env.CHATGPT_TEMPERATURE ?? 0;
64
+
65
+ export const topP = process.env.CHATGPT_TOPP ?? 0.5;
package/src/index.js ADDED
@@ -0,0 +1,33 @@
1
+ // Importing dotenv config to load environment variables from .env file
2
+ // eslint-disable-next-line no-unused-vars
3
+ import dotenv from 'dotenv/config';
4
+
5
+ export { default as Dismantle } from './chains/dismantle/index.js';
6
+ export { default as list } from './chains/list/index.js';
7
+
8
+ export { default as questions } from './chains/questions/index.js';
9
+ export { default as scanJS } from './chains/scan-js/index.js';
10
+ export { default as sort } from './chains/sort/index.js';
11
+ export { default as SummaryMap } from './chains/summary-map/index.js';
12
+
13
+ export { default as schemas } from './json-schemas/index.js';
14
+
15
+ import chatGPT from './lib/chatgpt/index.js';
16
+ export { default as retry } from './lib/retry/index.js';
17
+ export { default as stripResponse } from './lib/strip-response/index.js';
18
+ export { default as searchJSFiles } from './lib/search-js-files/index.js';
19
+
20
+ export * as prompts from './prompts/index.js';
21
+
22
+ export { getClient as getRedis } from './services/redis/index.js';
23
+
24
+ export { default as auto } from './verblets/auto/index.js';
25
+ export { default as bool } from './verblets/bool/index.js';
26
+ export { default as enums } from './verblets/enum/index.js';
27
+ export { default as intent } from './verblets/intent/index.js';
28
+ export { default as number } from './verblets/number/index.js';
29
+ export { default as schemaOrg } from './verblets/schema-org/index.js';
30
+ export { default as toObject } from './verblets/to-object/index.js';
31
+
32
+
33
+ export default chatGPT;
@@ -0,0 +1,11 @@
1
+ {
2
+ "type": "object",
3
+ "properties": {
4
+ "make": { "type": "string" },
5
+ "model": { "type": "string" },
6
+ "releaseDate": { "type": "string", "format": "date-time" },
7
+ "maxRange": { "type": "number", "description": "Max range in miles" },
8
+ "batteryCapacity": { "type": "number", "description": "Battery capacity in kWh" },
9
+ "startingCost": { "type": "number", "description": "Starting cost in USD" }
10
+ }
11
+ }
@@ -0,0 +1,18 @@
1
+ import fs from 'node:fs/promises';
2
+
3
+ const schemaFileNames = [
4
+ '../verblets/bool/index.schema.json',
5
+ '../chains/list/schema.json'
6
+ ];
7
+
8
+ const schemas = [];
9
+
10
+ for (let schemaFileName of schemaFileNames) {
11
+ const schemaText = await fs.readFile(
12
+ new URL(schemaFileName, import.meta.url)
13
+ );
14
+ const schema = JSON.parse(schemaText);
15
+ schemas.push(schema)
16
+ }
17
+
18
+ export default schemas;
@@ -0,0 +1,38 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "http://example.com/schemas/intent-schema.json",
4
+ "type": "object",
5
+ "properties": {
6
+ "queryText": {
7
+ "type": "string",
8
+ "description": "The user's query text."
9
+ },
10
+ "intent": {
11
+ "type": "object",
12
+ "properties": {
13
+ "operation": {
14
+ "type": "string",
15
+ "description": "The operation associated with the intent."
16
+ },
17
+ "displayName": {
18
+ "type": "string",
19
+ "description": "The display name of the intent."
20
+ }
21
+ },
22
+ "required": ["operation", "displayName"],
23
+ "description": "Information about the intent."
24
+ },
25
+ "parameters": {
26
+ "type": "object",
27
+ "additionalProperties": true,
28
+ "description": "Parameters associated with the intent."
29
+ },
30
+ "optionalParameters": {
31
+ "type": "object",
32
+ "additionalProperties": true,
33
+ "description": "Optional parameters associated with the intent."
34
+ }
35
+ },
36
+ "required": ["queryText", "intent"],
37
+ "description": "Schema for an intent response."
38
+ }