@far-world-labs/verblets 0.1.1 → 0.1.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.
- 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 +117 -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 +671 -0
- package/eslint.config.js +80 -0
- package/package.json +28 -16
- 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 +3 -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 +21 -41
- 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 +4 -4
- 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 +11 -16
- 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 +84 -1
- 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,134 @@
|
|
|
1
|
+
import list from '../list/index.js';
|
|
2
|
+
import retry from '../../lib/retry/index.js';
|
|
3
|
+
import modelService from '../../services/llm-model/index.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Core prompt template for sample generation using cognitive science principles
|
|
7
|
+
*/
|
|
8
|
+
export const SAMPLE_GENERATION_PROMPT = `Generate sample items for the category "{categoryName}" using cognitive science principles.
|
|
9
|
+
|
|
10
|
+
{context}
|
|
11
|
+
|
|
12
|
+
COGNITIVE PRINCIPLES:
|
|
13
|
+
1. Prototype Theory: Include items across the typicality spectrum
|
|
14
|
+
2. Family Resemblance: Ensure items share overlapping but not identical features
|
|
15
|
+
3. Category Structure: {diversityInstructions}
|
|
16
|
+
|
|
17
|
+
REQUIREMENTS:
|
|
18
|
+
- Include highly typical/prototypical members
|
|
19
|
+
- Include moderately typical members
|
|
20
|
+
- {diversityRequirement}
|
|
21
|
+
- Ensure good coverage of category space
|
|
22
|
+
- Avoid redundant or near-identical items
|
|
23
|
+
|
|
24
|
+
IMPORTANT: Return only clean item names without numbering, descriptions, or explanations.`;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Build sample generation prompt with specific parameters
|
|
28
|
+
* @param {string} categoryName - Name of the category
|
|
29
|
+
* @param {Object} config - Configuration options
|
|
30
|
+
* @returns {string} Complete prompt for sample generation
|
|
31
|
+
*/
|
|
32
|
+
export function buildSeedGenerationPrompt(
|
|
33
|
+
categoryName,
|
|
34
|
+
{ context = '', diversityLevel = 'balanced' } = {}
|
|
35
|
+
) {
|
|
36
|
+
const diversityInstructions = {
|
|
37
|
+
high: 'Include very diverse examples spanning edge cases and borderline members',
|
|
38
|
+
balanced: 'Include a mix of typical, moderately typical, and some atypical members',
|
|
39
|
+
focused: 'Focus on highly typical, central members with clear category membership',
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const diversityRequirement = {
|
|
43
|
+
high: 'Include many atypical but valid members',
|
|
44
|
+
balanced: 'Include some moderately atypical members',
|
|
45
|
+
focused: 'Focus primarily on typical members',
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const contextLine = context ? `Context: ${context}` : '';
|
|
49
|
+
|
|
50
|
+
return SAMPLE_GENERATION_PROMPT.replace('{categoryName}', categoryName)
|
|
51
|
+
.replace('{context}', contextLine)
|
|
52
|
+
.replace(
|
|
53
|
+
'{diversityInstructions}',
|
|
54
|
+
diversityInstructions[diversityLevel] || diversityInstructions.balanced
|
|
55
|
+
)
|
|
56
|
+
.replace(
|
|
57
|
+
'{diversityRequirement}',
|
|
58
|
+
diversityRequirement[diversityLevel] || diversityRequirement.balanced
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Generate sample items for a category using cognitive science principles.
|
|
64
|
+
* Creates diverse, representative examples across the typicality spectrum.
|
|
65
|
+
*
|
|
66
|
+
* @param {string} categoryName - Name of the category
|
|
67
|
+
* @param {Object} [options={}] - Configuration options
|
|
68
|
+
* @param {string} [options.context=''] - Context for sample generation
|
|
69
|
+
* @param {number} [options.count=30] - Number of sample items to generate
|
|
70
|
+
* @param {string} [options.diversityLevel='balanced'] - 'high', 'balanced', or 'focused'
|
|
71
|
+
* @param {string|Object} [options.llm='fastGoodCheap'] - LLM model to use
|
|
72
|
+
* @param {number} [options.maxRetries=3] - Maximum number of retry attempts
|
|
73
|
+
* @param {number} [options.retryDelay=1000] - Delay between retries in milliseconds
|
|
74
|
+
* @returns {Promise<string[]>}
|
|
75
|
+
*/
|
|
76
|
+
export default async function categorySamples(categoryName, options = {}) {
|
|
77
|
+
if (!categoryName || typeof categoryName !== 'string') {
|
|
78
|
+
throw new Error('categoryName must be a non-empty string');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const {
|
|
82
|
+
context = '',
|
|
83
|
+
count = 30,
|
|
84
|
+
diversityLevel = 'balanced',
|
|
85
|
+
llm = 'fastGoodCheap',
|
|
86
|
+
maxRetries = 3,
|
|
87
|
+
retryDelay = 1000,
|
|
88
|
+
} = options;
|
|
89
|
+
|
|
90
|
+
const generateWithRetry = async () => {
|
|
91
|
+
const prompt = buildSeedGenerationPrompt(categoryName, { context, diversityLevel });
|
|
92
|
+
|
|
93
|
+
// Get the model object from the model service
|
|
94
|
+
const model = typeof llm === 'string' ? modelService.getModel(llm) : llm;
|
|
95
|
+
|
|
96
|
+
const results = await list(prompt, {
|
|
97
|
+
llm: model,
|
|
98
|
+
shouldStop: ({ resultsAll }) => resultsAll.length >= count,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
if (!results || results.length === 0) {
|
|
102
|
+
throw new Error(`No sample items generated for category: ${categoryName}`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Return only the requested count
|
|
106
|
+
return results.slice(0, count);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
return await retry(generateWithRetry, {
|
|
110
|
+
maxRetries,
|
|
111
|
+
retryDelay,
|
|
112
|
+
retryCondition: (error) => {
|
|
113
|
+
// Retry on network errors, timeouts, or empty results
|
|
114
|
+
return (
|
|
115
|
+
error.message.includes('No sample items generated') ||
|
|
116
|
+
error.message.includes('timeout') ||
|
|
117
|
+
error.message.includes('network') ||
|
|
118
|
+
error.message.includes('ECONNRESET')
|
|
119
|
+
);
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Generate sample items for a category using list generation
|
|
126
|
+
* @param {string} category - The category to generate samples for
|
|
127
|
+
* @param {number} _count - Target number of samples (unused, kept for API compatibility)
|
|
128
|
+
* @param {Object} options - Additional options
|
|
129
|
+
* @returns {Promise<string[]>} Array of sample items
|
|
130
|
+
*/
|
|
131
|
+
export function categorySamplesList(category, _count = 10, options = {}) {
|
|
132
|
+
// Use the list chain to generate samples for the category
|
|
133
|
+
return list(category, options);
|
|
134
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# collect-terms
|
|
2
|
+
|
|
3
|
+
Extract the most difficult or technical terms from any passage. Useful for building a glossary or highlighting vocabulary that needs clarification.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
import collectTerms from './collect-terms/index.js';
|
|
9
|
+
|
|
10
|
+
const terms = await collectTerms(longText, { topN: 15 });
|
|
11
|
+
// => ['usufructuary rights', 'riparian', 'hydrological cycle', ...]
|
|
12
|
+
```
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { longTestTimeout } from '../../constants/common.js';
|
|
3
|
+
import collectTerms from './index.js';
|
|
4
|
+
|
|
5
|
+
const sample = `Quantum entanglement and the observer effect are central to modern physics. Understanding Heisenberg's uncertainty principle alongside quantum decoherence helps explain particle behavior at subatomic scales.`;
|
|
6
|
+
|
|
7
|
+
describe('collectTerms chain', () => {
|
|
8
|
+
it(
|
|
9
|
+
'Example',
|
|
10
|
+
async () => {
|
|
11
|
+
const terms = await collectTerms(sample, { topN: 5 });
|
|
12
|
+
expect(Array.isArray(terms)).toBe(true);
|
|
13
|
+
},
|
|
14
|
+
longTestTimeout
|
|
15
|
+
);
|
|
16
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import list from '../list/index.js';
|
|
2
|
+
import listReduce from '../../verblets/list-reduce/index.js';
|
|
3
|
+
|
|
4
|
+
const splitIntoChunks = (text, maxLen) => {
|
|
5
|
+
const words = text.split(/\s+/);
|
|
6
|
+
const chunks = [];
|
|
7
|
+
let current = '';
|
|
8
|
+
for (const word of words) {
|
|
9
|
+
if (current.length + word.length + 1 > maxLen) {
|
|
10
|
+
if (current) chunks.push(current.trim());
|
|
11
|
+
current = word;
|
|
12
|
+
} else {
|
|
13
|
+
current += (current ? ' ' : '') + word;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
if (current) chunks.push(current.trim());
|
|
17
|
+
return chunks;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default async function collectTerms(text, config = {}) {
|
|
21
|
+
const { chunkLen = 1000, topN = 20, llm, ...options } = config;
|
|
22
|
+
const chunks = splitIntoChunks(text, chunkLen);
|
|
23
|
+
const allTerms = [];
|
|
24
|
+
for (const chunk of chunks) {
|
|
25
|
+
const terms = await list(`important complex or technical terms from: ${chunk}`, {
|
|
26
|
+
llm,
|
|
27
|
+
...options,
|
|
28
|
+
});
|
|
29
|
+
allTerms.push(...terms);
|
|
30
|
+
}
|
|
31
|
+
const uniqueTerms = Array.from(new Set(allTerms.map((t) => t.trim())));
|
|
32
|
+
if (uniqueTerms.length <= topN) return uniqueTerms;
|
|
33
|
+
const reduced = await listReduce(
|
|
34
|
+
'',
|
|
35
|
+
uniqueTerms,
|
|
36
|
+
`Return the top ${topN} terms as a comma-separated list`,
|
|
37
|
+
{ llm, ...options }
|
|
38
|
+
);
|
|
39
|
+
return reduced
|
|
40
|
+
.split(',')
|
|
41
|
+
.map((t) => t.trim())
|
|
42
|
+
.filter(Boolean)
|
|
43
|
+
.slice(0, topN);
|
|
44
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { describe, expect, it, beforeEach, vi } from 'vitest';
|
|
2
|
+
import collectTerms from './index.js';
|
|
3
|
+
import list from '../list/index.js';
|
|
4
|
+
import listReduce from '../../verblets/list-reduce/index.js';
|
|
5
|
+
|
|
6
|
+
vi.mock('../list/index.js');
|
|
7
|
+
vi.mock('../../verblets/list-reduce/index.js');
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
vi.resetAllMocks();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
describe('collectTerms chain', () => {
|
|
14
|
+
it('deduplicates and reduces to top terms', async () => {
|
|
15
|
+
list.mockResolvedValueOnce(['alpha', 'beta']).mockResolvedValueOnce(['beta', 'gamma']);
|
|
16
|
+
listReduce.mockResolvedValue('alpha, beta, gamma');
|
|
17
|
+
|
|
18
|
+
const text = 'p1\n\np2';
|
|
19
|
+
const result = await collectTerms(text, { chunkLen: 2, topN: 2 });
|
|
20
|
+
|
|
21
|
+
expect(list).toHaveBeenCalledTimes(2);
|
|
22
|
+
expect(result).toStrictEqual(['alpha', 'beta']);
|
|
23
|
+
expect(listReduce).toHaveBeenCalled();
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# date
|
|
2
|
+
|
|
3
|
+
Iteratively refine LLM answers until they produce a valid JavaScript `Date` object that satisfies the prompt. Before asking for a date, the chain asks the language model to suggest a few quick checks that a correct answer should satisfy. Each returned date is evaluated against those expectations with the `bool` verblt and retried if any fail.
|
|
4
|
+
|
|
5
|
+
```javascript
|
|
6
|
+
import date from './index.js';
|
|
7
|
+
|
|
8
|
+
const release = await date('When was the original Star Wars film released?');
|
|
9
|
+
// => new Date('1977-05-25')
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
The chain asks for a date using prompt constants shared with primitive verblets. It then verifies the result with the `bool` verblet and retries until the date is deemed correct or attempts run out.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { describe, expect, it, beforeAll, afterAll } from 'vitest';
|
|
2
|
+
import date from './index.js';
|
|
3
|
+
import { expect as llmExpect } from '../../chains/expect/index.js';
|
|
4
|
+
import { longTestTimeout } from '../../constants/common.js';
|
|
5
|
+
|
|
6
|
+
describe('date examples', () => {
|
|
7
|
+
const originalMode = process.env.LLM_EXPECT_MODE;
|
|
8
|
+
|
|
9
|
+
beforeAll(() => {
|
|
10
|
+
process.env.LLM_EXPECT_MODE = 'none';
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
afterAll(() => {
|
|
14
|
+
if (originalMode !== undefined) {
|
|
15
|
+
process.env.LLM_EXPECT_MODE = originalMode;
|
|
16
|
+
} else {
|
|
17
|
+
delete process.env.LLM_EXPECT_MODE;
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it(
|
|
22
|
+
'gets Star Wars release date',
|
|
23
|
+
async () => {
|
|
24
|
+
const result = await date('When was the original Star Wars released?');
|
|
25
|
+
expect(result instanceof Date).toBe(true);
|
|
26
|
+
const [reasonable] = await llmExpect(
|
|
27
|
+
`Star Wars release date: ${result.toISOString()}`,
|
|
28
|
+
undefined,
|
|
29
|
+
'Is this close to the actual release date of the first Star Wars movie?'
|
|
30
|
+
);
|
|
31
|
+
expect(reasonable).toBe(true);
|
|
32
|
+
},
|
|
33
|
+
longTestTimeout
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
it(
|
|
37
|
+
'finds Christmas 2025',
|
|
38
|
+
async () => {
|
|
39
|
+
const result = await date('When is Christmas Day in 2025?');
|
|
40
|
+
expect(result instanceof Date).toBe(true);
|
|
41
|
+
expect(result.getUTCFullYear()).toBe(2025);
|
|
42
|
+
expect(result.getUTCMonth()).toBe(11); // December
|
|
43
|
+
expect(result.getUTCDate()).toBe(25);
|
|
44
|
+
},
|
|
45
|
+
longTestTimeout
|
|
46
|
+
);
|
|
47
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import chatGPT from '../../lib/chatgpt/index.js';
|
|
2
|
+
import stripResponse from '../../lib/strip-response/index.js';
|
|
3
|
+
import toDate from '../../lib/to-date/index.js';
|
|
4
|
+
import toObject from '../../verblets/to-object/index.js';
|
|
5
|
+
import bool from '../../verblets/bool/index.js';
|
|
6
|
+
import { constants as promptConstants } from '../../prompts/index.js';
|
|
7
|
+
|
|
8
|
+
const {
|
|
9
|
+
asDate,
|
|
10
|
+
asUndefinedByDefault,
|
|
11
|
+
contentIsQuestion,
|
|
12
|
+
explainAndSeparate,
|
|
13
|
+
explainAndSeparatePrimitive,
|
|
14
|
+
onlyJSONArray,
|
|
15
|
+
} = promptConstants;
|
|
16
|
+
|
|
17
|
+
const expectationPrompt = (question) => `${contentIsQuestion} ${question}
|
|
18
|
+
|
|
19
|
+
List up to three short yes/no checks that would confirm a date answer is correct. If nothing specific comes to mind, respond with ["The result is a valid date"].
|
|
20
|
+
|
|
21
|
+
${onlyJSONArray}`;
|
|
22
|
+
|
|
23
|
+
const buildCheckPrompt = (dateValue, check) => {
|
|
24
|
+
const iso = dateValue.toISOString();
|
|
25
|
+
const human = dateValue.toUTCString();
|
|
26
|
+
const utcDate = new Date(
|
|
27
|
+
Date.UTC(dateValue.getUTCFullYear(), dateValue.getUTCMonth(), dateValue.getUTCDate())
|
|
28
|
+
);
|
|
29
|
+
return `Date in ISO: ${iso} (UTC: ${human}, UTC date: ${utcDate.toISOString()}). Does this satisfy "${check}"?`;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default async function date(text, config = {}) {
|
|
33
|
+
const { maxAttempts = 3, llm, ...options } = config;
|
|
34
|
+
const llmExpectations = (await toObject(
|
|
35
|
+
await chatGPT(expectationPrompt(text), { modelOptions: { ...llm }, ...options }),
|
|
36
|
+
null,
|
|
37
|
+
{ llm, ...options }
|
|
38
|
+
)) || ['The result is a valid date'];
|
|
39
|
+
|
|
40
|
+
let attemptText = text;
|
|
41
|
+
let response;
|
|
42
|
+
for (let i = 0; i < maxAttempts; i += 1) {
|
|
43
|
+
const datePrompt = `${contentIsQuestion} ${attemptText}\n\n${explainAndSeparate} ${explainAndSeparatePrimitive}\n\n${asDate} ${asUndefinedByDefault}`;
|
|
44
|
+
// eslint-disable-next-line no-await-in-loop
|
|
45
|
+
response = await chatGPT(datePrompt, { modelOptions: { ...llm }, ...options });
|
|
46
|
+
const value = toDate(stripResponse(response));
|
|
47
|
+
if (value === undefined) return undefined;
|
|
48
|
+
|
|
49
|
+
// Convert to UTC date for consistent checks
|
|
50
|
+
const utcValue = new Date(
|
|
51
|
+
Date.UTC(value.getUTCFullYear(), value.getUTCMonth(), value.getUTCDate())
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
let failedCheck;
|
|
55
|
+
for (const check of llmExpectations) {
|
|
56
|
+
// eslint-disable-next-line no-await-in-loop
|
|
57
|
+
const passed = await bool(buildCheckPrompt(utcValue, check), { llm, ...options });
|
|
58
|
+
if (!passed) {
|
|
59
|
+
failedCheck = check;
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!failedCheck) return utcValue;
|
|
65
|
+
|
|
66
|
+
attemptText = `${text} The previous answer (${utcValue.toISOString()}) failed to satisfy: "${failedCheck}". Try again.`;
|
|
67
|
+
}
|
|
68
|
+
const finalValue = toDate(stripResponse(response));
|
|
69
|
+
return finalValue
|
|
70
|
+
? new Date(
|
|
71
|
+
Date.UTC(finalValue.getUTCFullYear(), finalValue.getUTCMonth(), finalValue.getUTCDate())
|
|
72
|
+
)
|
|
73
|
+
: undefined;
|
|
74
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import date from './index.js';
|
|
3
|
+
import bool from '../../verblets/bool/index.js';
|
|
4
|
+
|
|
5
|
+
vi.mock('../../lib/chatgpt/index.js', () => ({
|
|
6
|
+
default: vi.fn(),
|
|
7
|
+
}));
|
|
8
|
+
|
|
9
|
+
vi.mock('../../verblets/bool/index.js', () => ({
|
|
10
|
+
default: vi.fn(),
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
vi.mock('../../verblets/to-object/index.js', () => ({
|
|
14
|
+
default: vi.fn(),
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
const chatGPT = (await import('../../lib/chatgpt/index.js')).default;
|
|
18
|
+
const toObjectMock = (await import('../../verblets/to-object/index.js')).default;
|
|
19
|
+
|
|
20
|
+
describe('date chain', () => {
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
vi.clearAllMocks();
|
|
23
|
+
});
|
|
24
|
+
it('returns a date when bool approves', async () => {
|
|
25
|
+
chatGPT.mockResolvedValueOnce('["check"]');
|
|
26
|
+
toObjectMock.mockResolvedValueOnce(['check']);
|
|
27
|
+
chatGPT.mockResolvedValueOnce('2023-01-02');
|
|
28
|
+
bool.mockResolvedValueOnce(true);
|
|
29
|
+
const result = await date('When is tomorrow?');
|
|
30
|
+
expect(result instanceof Date).toBe(true);
|
|
31
|
+
expect(result.toISOString().startsWith('2023-01-02')).toBe(true);
|
|
32
|
+
expect(chatGPT).toHaveBeenCalledTimes(2);
|
|
33
|
+
expect(toObjectMock).toHaveBeenCalledTimes(1);
|
|
34
|
+
expect(bool).toHaveBeenCalledTimes(1);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('retries until bool approves', async () => {
|
|
38
|
+
chatGPT.mockResolvedValueOnce('["check"]');
|
|
39
|
+
toObjectMock.mockResolvedValueOnce(['check']);
|
|
40
|
+
chatGPT.mockResolvedValueOnce('2023-01-02');
|
|
41
|
+
chatGPT.mockResolvedValueOnce('2023-02-03');
|
|
42
|
+
bool.mockResolvedValueOnce(false);
|
|
43
|
+
bool.mockResolvedValueOnce(true);
|
|
44
|
+
|
|
45
|
+
const result = await date('When is tomorrow?', { maxAttempts: 2 });
|
|
46
|
+
expect(result.toISOString().startsWith('2023-02-03')).toBe(true);
|
|
47
|
+
expect(chatGPT).toHaveBeenCalledTimes(3);
|
|
48
|
+
expect(toObjectMock).toHaveBeenCalledTimes(1);
|
|
49
|
+
expect(bool).toHaveBeenCalledTimes(2);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('returns undefined when chatGPT says undefined', async () => {
|
|
53
|
+
chatGPT.mockResolvedValueOnce('["check"]');
|
|
54
|
+
toObjectMock.mockResolvedValueOnce(['check']);
|
|
55
|
+
chatGPT.mockResolvedValueOnce('undefined');
|
|
56
|
+
const result = await date('Unknown date');
|
|
57
|
+
expect(result).toBeUndefined();
|
|
58
|
+
expect(chatGPT).toHaveBeenCalledTimes(2);
|
|
59
|
+
expect(toObjectMock).toHaveBeenCalledTimes(1);
|
|
60
|
+
expect(bool).not.toHaveBeenCalled();
|
|
61
|
+
});
|
|
62
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# disambiguate
|
|
2
|
+
|
|
3
|
+
Determine the intended meaning of a polysemous word or short phrase based on surrounding context.
|
|
4
|
+
The chain lists common meanings and filters them down to the one that fits best.
|
|
5
|
+
|
|
6
|
+
```javascript
|
|
7
|
+
import disambiguate from './index.js';
|
|
8
|
+
|
|
9
|
+
const result = await disambiguate({
|
|
10
|
+
term: 'bat',
|
|
11
|
+
context: 'The child swung the bat at the baseball.'
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
console.log(result.meaning);
|
|
15
|
+
// => "a club used in sports like baseball"
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Use case: clarifying travel conversations
|
|
19
|
+
|
|
20
|
+
When a traveler says, "I spoke with the coach about my seat," it helps to know
|
|
21
|
+
whether they mean a sports instructor or an airline seating class. This chain
|
|
22
|
+
uses language model reasoning to resolve that ambiguity automatically.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"properties": {
|
|
5
|
+
"meanings": {
|
|
6
|
+
"type": "array",
|
|
7
|
+
"description": "Array of distinct dictionary meanings or common uses of a term",
|
|
8
|
+
"items": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"description": "A distinct meaning or common use of the term"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"required": ["meanings"],
|
|
15
|
+
"additionalProperties": false
|
|
16
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import disambiguate from './index.js';
|
|
3
|
+
import { longTestTimeout } from '../../constants/common.js';
|
|
4
|
+
|
|
5
|
+
describe('Disambiguate chain', () => {
|
|
6
|
+
it(
|
|
7
|
+
'contextual meaning: bank',
|
|
8
|
+
async () => {
|
|
9
|
+
const result = await disambiguate({
|
|
10
|
+
term: 'bank',
|
|
11
|
+
context: 'She waited in line at the bank to deposit her paycheck.',
|
|
12
|
+
});
|
|
13
|
+
expect(typeof result.meaning).toBe('string');
|
|
14
|
+
expect(result.meaning.length).toBeGreaterThan(0);
|
|
15
|
+
},
|
|
16
|
+
longTestTimeout
|
|
17
|
+
);
|
|
18
|
+
});
|
|
@@ -0,0 +1,92 @@
|
|
|
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 listFilter from '../../verblets/list-filter/index.js';
|
|
6
|
+
import { constants as promptConstants } from '../../prompts/index.js';
|
|
7
|
+
import modelService from '../../services/llm-model/index.js';
|
|
8
|
+
|
|
9
|
+
const { onlyJSONStringArray } = promptConstants;
|
|
10
|
+
|
|
11
|
+
// Get the directory of this module
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = path.dirname(__filename);
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Load the JSON schema for disambiguate meanings results
|
|
17
|
+
* @returns {Promise<Object>} JSON schema for validation
|
|
18
|
+
*/
|
|
19
|
+
async function getDisambiguateMeaningsSchema() {
|
|
20
|
+
const schemaPath = path.join(__dirname, 'disambiguate-meanings-result.json');
|
|
21
|
+
return JSON.parse(await fs.readFile(schemaPath, 'utf8'));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Create model options for structured outputs
|
|
26
|
+
* @param {string|Object} llm - LLM model name or configuration object
|
|
27
|
+
* @returns {Promise<Object>} Model options for chatGPT
|
|
28
|
+
*/
|
|
29
|
+
async function createModelOptions(llm = 'fastGoodCheap') {
|
|
30
|
+
const schema = await getDisambiguateMeaningsSchema();
|
|
31
|
+
|
|
32
|
+
const responseFormat = {
|
|
33
|
+
type: 'json_schema',
|
|
34
|
+
json_schema: {
|
|
35
|
+
name: 'disambiguate_meanings_result',
|
|
36
|
+
schema,
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
if (typeof llm === 'string') {
|
|
41
|
+
return {
|
|
42
|
+
modelName: llm,
|
|
43
|
+
response_format: responseFormat,
|
|
44
|
+
};
|
|
45
|
+
} else {
|
|
46
|
+
return {
|
|
47
|
+
...llm,
|
|
48
|
+
response_format: responseFormat,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const meaningsPrompt = (term) => {
|
|
54
|
+
return `${onlyJSONStringArray}
|
|
55
|
+
List all distinct dictionary meanings or common uses of "${term}".
|
|
56
|
+
Return a JSON object with a "meanings" array containing the distinct meanings.
|
|
57
|
+
${onlyJSONStringArray}`;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const getMeanings = async (term, config = {}) => {
|
|
61
|
+
const { model = modelService.getBestPublicModel(), llm, ...options } = config;
|
|
62
|
+
const prompt = meaningsPrompt(term);
|
|
63
|
+
const budget = model.budgetTokens(prompt);
|
|
64
|
+
const modelOptions = await createModelOptions(llm);
|
|
65
|
+
const response = await chatGPT(prompt, {
|
|
66
|
+
maxTokens: budget.completion,
|
|
67
|
+
modelOptions,
|
|
68
|
+
...options,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// With structured outputs, response should already be parsed and validated
|
|
72
|
+
const parsed = typeof response === 'string' ? JSON.parse(response) : response;
|
|
73
|
+
// Extract meanings from the object structure
|
|
74
|
+
const resultArray = parsed?.meanings || parsed;
|
|
75
|
+
return Array.isArray(resultArray) ? resultArray.filter(Boolean) : [];
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export default async function disambiguate({
|
|
79
|
+
term,
|
|
80
|
+
context,
|
|
81
|
+
model = modelService.getBestPublicModel(),
|
|
82
|
+
...config
|
|
83
|
+
} = {}) {
|
|
84
|
+
const { llm, ...options } = config;
|
|
85
|
+
const meanings = await getMeanings(term, { model, llm, ...options });
|
|
86
|
+
const best = await listFilter(
|
|
87
|
+
meanings,
|
|
88
|
+
`the meaning of "${term}" in context: ${context}. Keep only the single best matching meaning.`,
|
|
89
|
+
{ llm, ...options }
|
|
90
|
+
);
|
|
91
|
+
return { meaning: best[0], meanings };
|
|
92
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import disambiguate from './index.js';
|
|
3
|
+
|
|
4
|
+
vi.mock('../../lib/chatgpt/index.js', () => ({
|
|
5
|
+
default: vi.fn(async (prompt) => {
|
|
6
|
+
if (/List all distinct dictionary meanings/.test(prompt)) {
|
|
7
|
+
return '["financial institution","edge of a river"]';
|
|
8
|
+
}
|
|
9
|
+
return '[]';
|
|
10
|
+
}),
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
vi.mock('../../verblets/list-filter/index.js', () => ({
|
|
14
|
+
default: vi.fn(async (list) => {
|
|
15
|
+
return [list[0]];
|
|
16
|
+
}),
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
describe('disambiguate chain', () => {
|
|
20
|
+
it('selects meaning based on context', async () => {
|
|
21
|
+
const result = await disambiguate({ term: 'bank', context: 'withdraw money' });
|
|
22
|
+
expect(result.meaning).toBe('financial institution');
|
|
23
|
+
expect(result.meanings).toStrictEqual(['financial institution', 'edge of a river']);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# dismantle
|
|
2
|
+
|
|
3
|
+
Break down complex systems into a tree of components using an LLM. `dismantle` creates a `ChainTree` that you can grow to any depth or inspect piece by piece.
|
|
4
|
+
|
|
5
|
+
## Example
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
import { dismantle, simplifyTree } from './index.js';
|
|
9
|
+
|
|
10
|
+
// Break down complex systems into components
|
|
11
|
+
const chain = dismantle('AirPods Pro');
|
|
12
|
+
await chain.makeSubtree({ depth: 1 });
|
|
13
|
+
console.log(simplifyTree(chain.getTree()));
|
|
14
|
+
/* Returns:
|
|
15
|
+
{
|
|
16
|
+
id: '...',
|
|
17
|
+
name: 'AirPods Pro',
|
|
18
|
+
parts: [
|
|
19
|
+
{ id: '...', name: 'H2 Chip' },
|
|
20
|
+
{ id: '...', name: 'Drivers: custom_dynamic' },
|
|
21
|
+
{ id: '...', name: 'Noise Sensors' },
|
|
22
|
+
{ id: '...', name: 'Battery: lithium_ion' }
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
*/
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## API
|
|
29
|
+
|
|
30
|
+
### `dismantle(name, [options])`
|
|
31
|
+
Returns a `ChainTree` for `name`. `options` lets you override how components are discovered:
|
|
32
|
+
|
|
33
|
+
- `decompose(component)` – return an array of subcomponent names.
|
|
34
|
+
```javascript
|
|
35
|
+
const chain = dismantle('Web App', {
|
|
36
|
+
decompose: async ({ name }) => fetch(`/api/parts?for=${name}`).then(r => r.json())
|
|
37
|
+
});
|
|
38
|
+
```
|
|
39
|
+
- `enhance(component)` – enrich a node with metadata or known variants.
|
|
40
|
+
```javascript
|
|
41
|
+
const chain = dismantle('Server', {
|
|
42
|
+
enhance: async ({ name }) => ({ name, options: await lookUpVariants(name) })
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
- `makeId()` – create unique IDs (defaults to `uuid.v4`).
|
|
46
|
+
- `enhanceFixes` – text appended to LLM prompts used by `enhance`.
|
|
47
|
+
```javascript
|
|
48
|
+
const chain = dismantle('Bike', { enhanceFixes: 'Prefer standard part names.' });
|
|
49
|
+
```
|
|
50
|
+
- `decomposeFixes` – text appended to prompts used by `decompose`.
|
|
51
|
+
|
|
52
|
+
### `ChainTree`
|
|
53
|
+
Returned by `dismantle`. Methods:
|
|
54
|
+
|
|
55
|
+
- `makeSubtree({ depth })` – grow the tree from the root for a number of levels.
|
|
56
|
+
```javascript
|
|
57
|
+
await chain.makeSubtree({ depth: 2 });
|
|
58
|
+
```
|
|
59
|
+
- `attachSubtree({ find, depth })` – expand a node that matches a predicate.
|
|
60
|
+
```javascript
|
|
61
|
+
await chain.attachSubtree({ find: (n) => n.name === 'Battery', depth: 1 });
|
|
62
|
+
```
|
|
63
|
+
- `getTree()` – access the internal detailed tree.
|
|
64
|
+
|
|
65
|
+
### `simplifyTree(node)`
|
|
66
|
+
Convert a detailed node or tree into `{ id, name, parts }` for easy consumption.
|
|
67
|
+
|