@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.
- package/.cursor/launch.json +30 -0
- package/.cursor/settings.json +20 -0
- package/.github/workflows/branch-protection.yml +22 -0
- package/.github/workflows/ci.yml +120 -0
- package/.prettierrc +6 -0
- package/.release-it.json +4 -1
- package/.vscode/launch.json +31 -0
- package/AGENTS.md +220 -0
- package/DEVELOPING.md +105 -0
- package/README.md +254 -0
- package/eslint.config.js +80 -0
- package/package.json +29 -17
- package/scripts/generate-test/index.js +29 -3
- package/scripts/runner/index.js +26 -0
- package/scripts/simple-editor/index.js +29 -18
- package/scripts/summarize-files/index.js +28 -4
- package/src/chains/README.md +30 -0
- package/src/chains/anonymize/README.md +21 -0
- package/src/chains/anonymize/index.examples.js +75 -0
- package/src/chains/anonymize/index.js +121 -0
- package/src/chains/anonymize/index.spec.js +78 -0
- package/src/chains/bulk-central-tendency/index.examples.js +138 -0
- package/src/chains/bulk-central-tendency/index.js +91 -0
- package/src/chains/bulk-filter/README.md +21 -0
- package/src/chains/bulk-filter/index.examples.js +22 -0
- package/src/chains/bulk-filter/index.js +58 -0
- package/src/chains/bulk-filter/index.spec.js +38 -0
- package/src/chains/bulk-find/README.md +16 -0
- package/src/chains/bulk-find/index.examples.js +20 -0
- package/src/chains/bulk-find/index.js +30 -0
- package/src/chains/bulk-find/index.spec.js +26 -0
- package/src/chains/bulk-group/README.md +23 -0
- package/src/chains/bulk-group/index.examples.js +18 -0
- package/src/chains/bulk-group/index.js +34 -0
- package/src/chains/bulk-group/index.spec.js +41 -0
- package/src/chains/bulk-map/README.md +43 -0
- package/src/chains/bulk-map/index.examples.js +17 -0
- package/src/chains/bulk-map/index.js +86 -0
- package/src/chains/bulk-map/index.spec.js +44 -0
- package/src/chains/bulk-reduce/README.md +12 -0
- package/src/chains/bulk-reduce/index.examples.js +15 -0
- package/src/chains/bulk-reduce/index.js +13 -0
- package/src/chains/bulk-reduce/index.spec.js +25 -0
- package/src/chains/bulk-score/README.md +16 -0
- package/src/chains/bulk-score/bulk-score-result.json +18 -0
- package/src/chains/bulk-score/index.examples.js +22 -0
- package/src/chains/bulk-score/index.js +133 -0
- package/src/chains/bulk-score/index.spec.js +30 -0
- package/src/chains/category-samples/README.md +61 -0
- package/src/chains/category-samples/index.examples.js +103 -0
- package/src/chains/category-samples/index.js +134 -0
- package/src/chains/collect-terms/README.md +12 -0
- package/src/chains/collect-terms/index.examples.js +16 -0
- package/src/chains/collect-terms/index.js +44 -0
- package/src/chains/collect-terms/index.spec.js +25 -0
- package/src/chains/date/README.md +12 -0
- package/src/chains/date/index.examples.js +47 -0
- package/src/chains/date/index.js +74 -0
- package/src/chains/date/index.spec.js +62 -0
- package/src/chains/disambiguate/README.md +22 -0
- package/src/chains/disambiguate/disambiguate-meanings-result.json +16 -0
- package/src/chains/disambiguate/index.examples.js +18 -0
- package/src/chains/disambiguate/index.js +92 -0
- package/src/chains/disambiguate/index.spec.js +25 -0
- package/src/chains/dismantle/README.md +67 -0
- package/src/chains/dismantle/dismantle.examples.js +27 -0
- package/src/chains/dismantle/index.js +6 -17
- package/src/chains/dismantle/index.spec.js +1 -2
- package/src/chains/expect/README.md +171 -0
- package/src/chains/expect/index.examples.js +146 -0
- package/src/chains/expect/index.js +173 -0
- package/src/chains/expect/index.spec.js +324 -0
- package/src/chains/filter-ambiguous/README.md +11 -0
- package/src/chains/filter-ambiguous/index.examples.js +20 -0
- package/src/chains/filter-ambiguous/index.js +49 -0
- package/src/chains/filter-ambiguous/index.spec.js +31 -0
- package/src/chains/glossary/README.md +19 -0
- package/src/chains/glossary/index.examples.js +386 -0
- package/src/chains/glossary/index.js +75 -0
- package/src/chains/glossary/index.spec.js +19 -0
- package/src/chains/intersections/README.md +152 -0
- package/src/chains/intersections/index.examples.js +279 -0
- package/src/chains/intersections/index.js +366 -0
- package/src/chains/intersections/intersection-result.json +38 -0
- package/src/chains/list/index.examples.js +12 -16
- package/src/chains/list/index.js +106 -53
- package/src/chains/list/index.spec.js +8 -9
- package/src/chains/list/list-result.json +16 -0
- package/src/chains/llm-logger/README.md +208 -0
- package/src/chains/llm-logger/index.js +205 -0
- package/src/chains/llm-logger/index.spec.js +330 -0
- package/src/chains/questions/index.examples.js +2 -1
- package/src/chains/questions/index.js +14 -15
- package/src/chains/scan-js/index.js +6 -9
- package/src/chains/set-interval/README.md +81 -0
- package/src/chains/set-interval/index.examples.js +36 -0
- package/src/chains/set-interval/index.js +131 -0
- package/src/chains/set-interval/index.spec.js +70 -0
- package/src/chains/socratic/README.md +17 -0
- package/src/chains/socratic/index.js +64 -0
- package/src/chains/socratic/index.spec.js +24 -0
- package/src/chains/sort/index.examples.js +3 -7
- package/src/chains/sort/index.js +65 -15
- package/src/chains/sort/index.spec.js +5 -8
- package/src/chains/sort/sort-result.json +16 -0
- package/src/chains/summary-map/README.md +9 -1
- package/src/chains/summary-map/index.examples.js +9 -2
- package/src/chains/summary-map/index.js +43 -25
- package/src/chains/summary-map/index.spec.js +78 -3
- package/src/chains/test/index.js +9 -13
- package/src/chains/test-advice/index.js +4 -5
- package/src/chains/themes/README.md +20 -0
- package/src/chains/themes/index.examples.js +17 -0
- package/src/chains/themes/index.js +28 -0
- package/src/chains/themes/index.spec.js +19 -0
- package/src/chains/veiled-variants/index.examples.js +18 -0
- package/src/chains/veiled-variants/index.js +107 -0
- package/src/chains/veiled-variants/index.spec.js +40 -0
- package/src/constants/common.js +0 -2
- package/src/constants/models.js +172 -0
- package/src/index.js +178 -18
- package/src/json-schemas/README.md +13 -0
- package/src/json-schemas/index.js +8 -14
- package/src/json-schemas/schema-dot-org-photograph.json +11 -5
- package/src/json-schemas/schema-dot-org-place.json +78 -5
- package/src/lib/README.md +26 -0
- package/src/lib/bulk-filter/README.md +22 -0
- package/src/lib/bulk-filter/index.examples.js +27 -0
- package/src/lib/bulk-filter/index.js +63 -0
- package/src/lib/bulk-filter/index.spec.js +38 -0
- package/src/lib/bulk-find/README.md +18 -0
- package/src/lib/bulk-find/index.examples.js +19 -0
- package/src/lib/bulk-find/index.js +30 -0
- package/src/lib/bulk-find/index.spec.js +41 -0
- package/src/lib/chatgpt/index.js +63 -43
- package/src/lib/combinations/index.js +30 -0
- package/src/lib/combinations/index.spec.js +23 -0
- package/src/lib/functional/index.js +28 -0
- package/src/lib/logger-service/index.js +32 -0
- package/src/lib/parse-js-parts/index.js +9 -21
- package/src/lib/parse-llm-list/README.md +39 -0
- package/src/lib/parse-llm-list/index.js +54 -0
- package/src/lib/parse-llm-list/index.spec.js +59 -0
- package/src/lib/path-aliases/index.js +1 -3
- package/src/lib/path-aliases/index.spec.js +2 -8
- package/src/lib/pave/index.js +4 -4
- package/src/lib/pave/index.spec.js +6 -3
- package/src/lib/prompt-cache/index.js +14 -10
- package/src/lib/retry/index.js +11 -8
- package/src/lib/ring-buffer/README.md +460 -0
- package/src/lib/ring-buffer/index.js +1074 -0
- package/src/lib/search-best-first/city-walk.spec.js +37 -0
- package/src/lib/search-best-first/index.js +42 -11
- package/src/lib/search-best-first/index.spec.js +35 -0
- package/src/lib/search-js-files/index.js +44 -47
- package/src/lib/search-js-files/scan-file.js +10 -21
- package/src/lib/shorten-text/index.js +2 -7
- package/src/lib/shorten-text/index.spec.js +3 -3
- package/src/lib/strip-response/index.js +2 -7
- package/src/lib/template-replace/index.js +23 -0
- package/src/lib/template-replace/index.spec.js +60 -0
- package/src/lib/to-date/index.js +11 -0
- package/src/lib/to-number/index.js +1 -1
- package/src/lib/transcribe/index.js +26 -9
- package/src/prompts/README.md +3 -1
- package/src/prompts/as-object-with-schema.js +3 -8
- package/src/prompts/as-schema-org-text.js +10 -2
- package/src/prompts/code-features.js +1 -5
- package/src/prompts/constants.js +27 -27
- package/src/prompts/generate-collection.js +1 -1
- package/src/prompts/intent.js +16 -22
- package/src/prompts/select-from-threshold.js +1 -2
- package/src/prompts/sort.js +4 -8
- package/src/prompts/style.js +4 -7
- package/src/prompts/wrap-list.js +1 -4
- package/src/services/llm-model/global-overrides.spec.js +432 -0
- package/src/services/llm-model/index.js +234 -40
- package/src/services/llm-model/model.js +2 -2
- package/src/services/llm-model/negotiate.spec.js +447 -0
- package/src/services/redis/index.js +70 -7
- package/src/test/setup.js +20 -0
- package/src/verblets/README.md +26 -0
- package/src/verblets/auto/index.examples.js +12 -9
- package/src/verblets/auto/index.js +10 -10
- package/src/verblets/auto/index.spec.js +4 -6
- package/src/verblets/bool/README.md +36 -0
- package/src/verblets/bool/index.examples.js +53 -1
- package/src/verblets/bool/index.js +6 -9
- package/src/verblets/bool/index.spec.js +1 -3
- package/src/verblets/central-tendency/README.md +166 -0
- package/src/verblets/central-tendency/central-tendency-result.json +24 -0
- package/src/verblets/central-tendency/index.examples.js +196 -0
- package/src/verblets/central-tendency/index.js +171 -0
- package/src/verblets/central-tendency/index.spec.js +148 -0
- package/src/verblets/enum/index.examples.js +1 -4
- package/src/verblets/enum/index.js +7 -4
- package/src/verblets/expect/README.md +64 -0
- package/src/verblets/expect/index.examples.js +109 -0
- package/src/verblets/expect/index.js +75 -0
- package/src/verblets/expect/index.spec.js +127 -0
- package/src/verblets/intent/index.examples.js +95 -7
- package/src/verblets/intent/index.js +56 -68
- package/src/verblets/intersection/README.md +16 -0
- package/src/verblets/intersection/index.examples.js +89 -0
- package/src/verblets/intersection/index.js +84 -0
- package/src/verblets/intersection/index.spec.js +60 -0
- package/src/verblets/intersection/intersection-result.json +16 -0
- package/src/verblets/list-expand/README.md +10 -0
- package/src/verblets/list-expand/index.examples.js +14 -0
- package/src/verblets/list-expand/index.js +104 -0
- package/src/verblets/list-expand/index.spec.js +18 -0
- package/src/verblets/list-expand/list-expand-result.json +16 -0
- package/src/verblets/list-filter/README.md +22 -0
- package/src/verblets/list-filter/index.examples.js +26 -0
- package/src/verblets/list-filter/index.js +18 -0
- package/src/verblets/list-filter/index.spec.js +19 -0
- package/src/verblets/list-find/README.md +11 -0
- package/src/verblets/list-find/index.examples.js +15 -0
- package/src/verblets/list-find/index.js +17 -0
- package/src/verblets/list-find/index.spec.js +19 -0
- package/src/verblets/list-group/README.md +16 -0
- package/src/verblets/list-group/index.examples.js +16 -0
- package/src/verblets/list-group/index.js +112 -0
- package/src/verblets/list-group/index.spec.js +35 -0
- package/src/verblets/list-group/list-group-result.json +16 -0
- package/src/verblets/list-map/README.md +11 -0
- package/src/verblets/list-map/index.examples.js +15 -0
- package/src/verblets/list-map/index.js +26 -0
- package/src/verblets/list-map/index.spec.js +17 -0
- package/src/verblets/list-reduce/README.md +10 -0
- package/src/verblets/list-reduce/index.examples.js +14 -0
- package/src/verblets/list-reduce/index.js +21 -0
- package/src/verblets/list-reduce/index.spec.js +27 -0
- package/src/verblets/list-reduce/index.spec.jsx +27 -0
- package/src/verblets/name/README.md +15 -0
- package/src/verblets/name/index.examples.js +28 -0
- package/src/verblets/name/index.js +19 -0
- package/src/verblets/name/index.spec.js +33 -0
- package/src/verblets/name-similar-to/README.md +26 -0
- package/src/verblets/name-similar-to/index.examples.js +18 -0
- package/src/verblets/name-similar-to/index.js +20 -0
- package/src/verblets/name-similar-to/index.spec.js +13 -0
- package/src/verblets/number/index.examples.js +173 -7
- package/src/verblets/number/index.js +5 -2
- package/src/verblets/number/index.spec.js +1 -3
- package/src/verblets/number-with-units/index.examples.js +5 -1
- package/src/verblets/number-with-units/index.js +74 -9
- package/src/verblets/number-with-units/number-with-units-result.json +23 -0
- package/src/verblets/schema-org/index.examples.js +2 -7
- package/src/verblets/schema-org/index.js +32 -3
- package/src/verblets/sentiment/README.md +10 -0
- package/src/verblets/sentiment/index.examples.js +20 -0
- package/src/verblets/sentiment/index.js +9 -0
- package/src/verblets/sentiment/index.spec.js +20 -0
- package/src/verblets/to-object/index.js +10 -15
- package/src/verblets/to-object/index.spec.js +1 -4
- package/.eslintrc.json +0 -42
- package/docs/README.md +0 -41
- package/docs/babel.config.js +0 -3
- package/docs/blog/2019-05-28-first-blog-post.md +0 -12
- package/docs/blog/2019-05-29-long-blog-post.md +0 -44
- package/docs/blog/2021-08-01-mdx-blog-post.mdx +0 -20
- package/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg +0 -0
- package/docs/blog/2021-08-26-welcome/index.md +0 -25
- package/docs/blog/authors.yml +0 -17
- package/docs/docs/api/bool.md +0 -74
- package/docs/docs/api/search.md +0 -51
- package/docs/docs/intro.md +0 -47
- package/docs/docs/tutorial-basics/_category_.json +0 -8
- package/docs/docs/tutorial-basics/congratulations.md +0 -23
- package/docs/docs/tutorial-basics/create-a-blog-post.md +0 -34
- package/docs/docs/tutorial-basics/create-a-document.md +0 -57
- package/docs/docs/tutorial-basics/create-a-page.md +0 -43
- package/docs/docs/tutorial-basics/deploy-your-site.md +0 -31
- package/docs/docs/tutorial-basics/markdown-features.mdx +0 -152
- package/docs/docs/tutorial-extras/_category_.json +0 -7
- package/docs/docs/tutorial-extras/img/docsVersionDropdown.png +0 -0
- package/docs/docs/tutorial-extras/img/localeDropdown.png +0 -0
- package/docs/docs/tutorial-extras/manage-docs-versions.md +0 -55
- package/docs/docs/tutorial-extras/translate-your-site.md +0 -88
- package/docs/docusaurus.config.js +0 -120
- package/docs/package.json +0 -44
- package/docs/sidebars.js +0 -31
- package/docs/src/components/HomepageFeatures/index.js +0 -61
- package/docs/src/components/HomepageFeatures/styles.module.css +0 -11
- package/docs/src/css/custom.css +0 -30
- package/docs/src/pages/index.js +0 -43
- package/docs/src/pages/index.module.css +0 -23
- package/docs/src/pages/markdown-page.md +0 -7
- package/docs/static/.nojekyll +0 -0
- package/docs/static/img/docusaurus-social-card.jpg +0 -0
- package/docs/static/img/docusaurus.png +0 -0
- package/docs/static/img/favicon.ico +0 -0
- package/docs/static/img/logo.svg +0 -1
- package/docs/static/img/undraw_docusaurus_mountain.svg +0 -171
- package/docs/static/img/undraw_docusaurus_react.svg +0 -170
- package/docs/static/img/undraw_docusaurus_tree.svg +0 -40
- package/src/constants/openai.js +0 -65
- /package/{.vite.config.examples.js → .vitest.config.examples.js} +0 -0
- /package/{.vite.config.js → .vitest.config.js} +0 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse a list of items from LLM response that could be JSON array or CSV format.
|
|
3
|
+
* Handles common LLM response patterns and filters out invalid entries.
|
|
4
|
+
*
|
|
5
|
+
* @param {string} response - The LLM response string
|
|
6
|
+
* @param {object} [options] - Parsing options
|
|
7
|
+
* @param {string[]} [options.excludeValues=['none', 'null', 'undefined']] - Values to exclude (case-insensitive)
|
|
8
|
+
* @param {boolean} [options.trimItems=true] - Whether to trim whitespace from items
|
|
9
|
+
* @param {boolean} [options.filterEmpty=true] - Whether to filter out empty strings
|
|
10
|
+
* @returns {string[]} Array of parsed items
|
|
11
|
+
*/
|
|
12
|
+
export default function parseLLMList(
|
|
13
|
+
response,
|
|
14
|
+
{ excludeValues = ['none', 'null', 'undefined'], trimItems = true, filterEmpty = true } = {}
|
|
15
|
+
) {
|
|
16
|
+
if (!response || typeof response !== 'string') {
|
|
17
|
+
return [];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Skip responses that contain notes or are empty arrays
|
|
21
|
+
if (response === '[]' || response.includes('<note>')) {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const items = [];
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
// First try to parse as JSON array
|
|
29
|
+
const parsed = JSON.parse(response);
|
|
30
|
+
if (Array.isArray(parsed)) {
|
|
31
|
+
items.push(...parsed.filter((item) => typeof item === 'string'));
|
|
32
|
+
}
|
|
33
|
+
} catch {
|
|
34
|
+
// Fallback to comma-separated parsing if JSON fails
|
|
35
|
+
items.push(...response.split(','));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Process the items
|
|
39
|
+
let processedItems = items;
|
|
40
|
+
|
|
41
|
+
if (trimItems) {
|
|
42
|
+
processedItems = processedItems.map((item) => item.trim());
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (filterEmpty) {
|
|
46
|
+
processedItems = processedItems.filter(Boolean);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Filter out excluded values (case-insensitive)
|
|
50
|
+
const excludeSet = new Set(excludeValues.map((val) => val.toLowerCase()));
|
|
51
|
+
processedItems = processedItems.filter((item) => !excludeSet.has(item.toLowerCase()));
|
|
52
|
+
|
|
53
|
+
return processedItems;
|
|
54
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import parseLLMList from './index.js';
|
|
3
|
+
|
|
4
|
+
describe('parseLLMList', () => {
|
|
5
|
+
it('parses JSON array format', () => {
|
|
6
|
+
const input = '["term1", "term2", "term3"]';
|
|
7
|
+
const result = parseLLMList(input);
|
|
8
|
+
expect(result).toEqual(['term1', 'term2', 'term3']);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('parses CSV format', () => {
|
|
12
|
+
const input = 'term1, term2, term3';
|
|
13
|
+
const result = parseLLMList(input);
|
|
14
|
+
expect(result).toEqual(['term1', 'term2', 'term3']);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('handles empty array response', () => {
|
|
18
|
+
const input = '[]';
|
|
19
|
+
const result = parseLLMList(input);
|
|
20
|
+
expect(result).toEqual([]);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('handles responses with notes', () => {
|
|
24
|
+
const input = '<note>No terms found</note>';
|
|
25
|
+
const result = parseLLMList(input);
|
|
26
|
+
expect(result).toEqual([]);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('filters out excluded values', () => {
|
|
30
|
+
const input = 'term1, none, term2, null, term3';
|
|
31
|
+
const result = parseLLMList(input);
|
|
32
|
+
expect(result).toEqual(['term1', 'term2', 'term3']);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('trims whitespace from items', () => {
|
|
36
|
+
const input = ' term1 , term2 , term3 ';
|
|
37
|
+
const result = parseLLMList(input);
|
|
38
|
+
expect(result).toEqual(['term1', 'term2', 'term3']);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('filters out empty strings', () => {
|
|
42
|
+
const input = 'term1, , term2, , term3';
|
|
43
|
+
const result = parseLLMList(input);
|
|
44
|
+
expect(result).toEqual(['term1', 'term2', 'term3']);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('handles custom exclude values', () => {
|
|
48
|
+
const input = 'term1, n/a, term2, unknown, term3';
|
|
49
|
+
const result = parseLLMList(input, { excludeValues: ['n/a', 'unknown'] });
|
|
50
|
+
expect(result).toEqual(['term1', 'term2', 'term3']);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('handles invalid input gracefully', () => {
|
|
54
|
+
expect(parseLLMList(null)).toEqual([]);
|
|
55
|
+
expect(parseLLMList(undefined)).toEqual([]);
|
|
56
|
+
expect(parseLLMList('')).toEqual([]);
|
|
57
|
+
expect(parseLLMList(123)).toEqual([]);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -24,9 +24,7 @@ export default (sequences, delimiter = defaultDelimiter) => {
|
|
|
24
24
|
|
|
25
25
|
for (let i = 1; i <= splitSequence.length; i += 1) {
|
|
26
26
|
const tail = splitSequence.slice(-i).join(delimiter);
|
|
27
|
-
const conflictingSequences = sequences.filter((seq) =>
|
|
28
|
-
seq.endsWith(tail)
|
|
29
|
-
);
|
|
27
|
+
const conflictingSequences = sequences.filter((seq) => seq.endsWith(tail));
|
|
30
28
|
|
|
31
29
|
if (conflictingSequences.length === 1) {
|
|
32
30
|
tailsUnique[sequence] = tail;
|
|
@@ -45,14 +45,8 @@ describe('Path aliases', async () => {
|
|
|
45
45
|
examples.forEach((example) => {
|
|
46
46
|
it(example.name, () => {
|
|
47
47
|
const got = alias(example.inputs.sequences, example.inputs.delimiter);
|
|
48
|
-
const gotSorted = R.sort(
|
|
49
|
-
|
|
50
|
-
Object.entries(got)
|
|
51
|
-
);
|
|
52
|
-
const wantSorted = R.sort(
|
|
53
|
-
([k1], [k2]) => k1.localeCompare(k2),
|
|
54
|
-
Object.entries(example.want)
|
|
55
|
-
);
|
|
48
|
+
const gotSorted = R.sort(([k1], [k2]) => k1.localeCompare(k2), Object.entries(got));
|
|
49
|
+
const wantSorted = R.sort(([k1], [k2]) => k1.localeCompare(k2), Object.entries(example.want));
|
|
56
50
|
|
|
57
51
|
expect(gotSorted).toStrictEqual(wantSorted);
|
|
58
52
|
});
|
package/src/lib/pave/index.js
CHANGED
|
@@ -15,16 +15,16 @@ export default (obj, path, value) => {
|
|
|
15
15
|
const objNew = JSON.parse(JSON.stringify(obj));
|
|
16
16
|
let objMutating = objNew;
|
|
17
17
|
|
|
18
|
+
const isNumeric = (str) => /^\d+$/.test(str);
|
|
19
|
+
|
|
18
20
|
for (let i = 0; i < keys.length; i += 1) {
|
|
19
|
-
const key =
|
|
20
|
-
? keys[i]
|
|
21
|
-
: parseInt(keys[i], 10);
|
|
21
|
+
const key = isNumeric(keys[i]) ? parseInt(keys[i], 10) : keys[i];
|
|
22
22
|
|
|
23
23
|
if (i === keys.length - 1) {
|
|
24
24
|
objMutating[key] = value;
|
|
25
25
|
} else {
|
|
26
26
|
if (!objMutating[key]) {
|
|
27
|
-
objMutating[key] =
|
|
27
|
+
objMutating[key] = isNumeric(keys[i + 1]) ? [] : {};
|
|
28
28
|
}
|
|
29
29
|
objMutating = objMutating[key];
|
|
30
30
|
}
|
|
@@ -53,6 +53,11 @@ const examples = [
|
|
|
53
53
|
inputs: { obj: {}, path: 'a', value: 42 },
|
|
54
54
|
want: { result: { a: 42 } },
|
|
55
55
|
},
|
|
56
|
+
{
|
|
57
|
+
name: 'Handle numeric-like string keys',
|
|
58
|
+
inputs: { obj: {}, path: 'a.1b.c', value: 42 },
|
|
59
|
+
want: { result: { a: { '1b': { c: 42 } } } },
|
|
60
|
+
},
|
|
56
61
|
];
|
|
57
62
|
|
|
58
63
|
describe('pave', () => {
|
|
@@ -61,9 +66,7 @@ describe('pave', () => {
|
|
|
61
66
|
const { obj } = example.inputs;
|
|
62
67
|
|
|
63
68
|
if (example.want.throws) {
|
|
64
|
-
expect(() =>
|
|
65
|
-
pave(obj, example.inputs.path, example.inputs.value)
|
|
66
|
-
).toThrow();
|
|
69
|
+
expect(() => pave(obj, example.inputs.path, example.inputs.value)).toThrow();
|
|
67
70
|
} else {
|
|
68
71
|
const result = pave(obj, example.inputs.path, example.inputs.value);
|
|
69
72
|
expect(result).toEqual(example.want.result);
|
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import crypto from 'node:crypto';
|
|
2
2
|
import * as R from 'ramda';
|
|
3
3
|
|
|
4
|
-
import { cacheTTL } from '../../constants/
|
|
4
|
+
import { cacheTTL } from '../../constants/models.js';
|
|
5
5
|
|
|
6
6
|
const variableKeys = ['created', 'id', 'max_tokens', 'usage'];
|
|
7
7
|
|
|
8
8
|
const sortKeys = (data) => {
|
|
9
9
|
const sortedData = R.sortBy(R.identity, R.keys(data)).reduce((acc, key) => {
|
|
10
|
-
|
|
10
|
+
if (key === 'messages') {
|
|
11
|
+
// Preserve all messages and their roles
|
|
12
|
+
acc[key] = data[key].map((msg) => ({
|
|
13
|
+
role: msg.role,
|
|
14
|
+
content: msg.content,
|
|
15
|
+
}));
|
|
16
|
+
} else {
|
|
17
|
+
acc[key] = data[key];
|
|
18
|
+
}
|
|
11
19
|
return acc;
|
|
12
20
|
}, {});
|
|
13
21
|
|
|
@@ -23,9 +31,7 @@ export const toKey = (data) => {
|
|
|
23
31
|
};
|
|
24
32
|
|
|
25
33
|
export const get = async (redis, inputData) => {
|
|
26
|
-
const resultFromRedis = await redis.get(
|
|
27
|
-
toKey(R.omit(variableKeys, inputData))
|
|
28
|
-
);
|
|
34
|
+
const resultFromRedis = await redis.get(toKey(R.omit(variableKeys, inputData)));
|
|
29
35
|
|
|
30
36
|
const foundInRedis = !!resultFromRedis;
|
|
31
37
|
|
|
@@ -38,9 +44,7 @@ export const get = async (redis, inputData) => {
|
|
|
38
44
|
};
|
|
39
45
|
|
|
40
46
|
export const set = async (redis, inputData, outputData) => {
|
|
41
|
-
await redis.set(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
{ EX: cacheTTL }
|
|
45
|
-
);
|
|
47
|
+
await redis.set(toKey(R.omit(variableKeys, inputData)), JSON.stringify(outputData), {
|
|
48
|
+
EX: cacheTTL,
|
|
49
|
+
});
|
|
46
50
|
};
|
package/src/lib/retry/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable no-await-in-loop */
|
|
2
1
|
import {
|
|
3
2
|
maxRetries as maxRetriesDefault,
|
|
4
3
|
retryDelay as retryDelayDefault,
|
|
@@ -16,8 +15,10 @@ export default async (
|
|
|
16
15
|
let retry = 0;
|
|
17
16
|
let lastError = new Error('Nothing to run');
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
const sleep = (ms) =>
|
|
19
|
+
new Promise((resolve) => {
|
|
20
|
+
setTimeout(resolve, ms);
|
|
21
|
+
});
|
|
21
22
|
|
|
22
23
|
const labelDisplay = label ? `"${label}"` : '';
|
|
23
24
|
|
|
@@ -27,14 +28,15 @@ export default async (
|
|
|
27
28
|
const variables = [`retry: ${retry}`].join(', ');
|
|
28
29
|
const startTag = `${retry > 0 ? 'retry' : 'started'}`;
|
|
29
30
|
const startVariablesDisplay = `${retry > 0 ? ` (${variables})` : ''}`;
|
|
30
|
-
console
|
|
31
|
-
|
|
32
|
-
);
|
|
31
|
+
// eslint-disable-next-line no-console
|
|
32
|
+
console.error(`Run ${labelDisplay} [${startTag}]${startVariablesDisplay}`);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
// eslint-disable-next-line no-await-in-loop
|
|
35
36
|
const result = await fn();
|
|
36
37
|
|
|
37
38
|
if (label) {
|
|
39
|
+
// eslint-disable-next-line no-console
|
|
38
40
|
console.error(`Run ${labelDisplay} [complete]`);
|
|
39
41
|
}
|
|
40
42
|
|
|
@@ -42,10 +44,10 @@ export default async (
|
|
|
42
44
|
} catch (error) {
|
|
43
45
|
lastError = error;
|
|
44
46
|
|
|
45
|
-
const isRetry =
|
|
46
|
-
retryOnAll || (error.response && error.response.status === 429);
|
|
47
|
+
const isRetry = retryOnAll || (error.response && error.response.status === 429);
|
|
47
48
|
|
|
48
49
|
if (isRetry) {
|
|
50
|
+
// eslint-disable-next-line no-await-in-loop
|
|
49
51
|
await sleep(retryDelay * retry);
|
|
50
52
|
retry += 1;
|
|
51
53
|
} else {
|
|
@@ -54,6 +56,7 @@ export default async (
|
|
|
54
56
|
const doneTag = `${retry >= maxRetries ? 'abort' : 'retry'}`;
|
|
55
57
|
|
|
56
58
|
if (label) {
|
|
59
|
+
// eslint-disable-next-line no-console
|
|
57
60
|
console.error(`Run ${labelDisplay} [${doneTag}]: ${error.message}`);
|
|
58
61
|
}
|
|
59
62
|
}
|