@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.
- package/.eslintrc.json +42 -0
- package/.husky/pre-commit +4 -0
- package/.release-it.json +9 -0
- package/.vite.config.examples.js +8 -0
- package/.vite.config.js +8 -0
- package/docker-compose.yml +7 -0
- package/docs/README.md +41 -0
- package/docs/babel.config.js +3 -0
- package/docs/blog/2019-05-28-first-blog-post.md +12 -0
- package/docs/blog/2019-05-29-long-blog-post.md +44 -0
- package/docs/blog/2021-08-01-mdx-blog-post.mdx +20 -0
- package/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg +0 -0
- package/docs/blog/2021-08-26-welcome/index.md +25 -0
- package/docs/blog/authors.yml +17 -0
- package/docs/docs/api/bool.md +74 -0
- package/docs/docs/api/search.md +51 -0
- package/docs/docs/intro.md +47 -0
- package/docs/docs/tutorial-basics/_category_.json +8 -0
- package/docs/docs/tutorial-basics/congratulations.md +23 -0
- package/docs/docs/tutorial-basics/create-a-blog-post.md +34 -0
- package/docs/docs/tutorial-basics/create-a-document.md +57 -0
- package/docs/docs/tutorial-basics/create-a-page.md +43 -0
- package/docs/docs/tutorial-basics/deploy-your-site.md +31 -0
- package/docs/docs/tutorial-basics/markdown-features.mdx +152 -0
- package/docs/docs/tutorial-extras/_category_.json +7 -0
- 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 +55 -0
- package/docs/docs/tutorial-extras/translate-your-site.md +88 -0
- package/docs/docusaurus.config.js +120 -0
- package/docs/package.json +44 -0
- package/docs/sidebars.js +31 -0
- package/docs/src/components/HomepageFeatures/index.js +61 -0
- package/docs/src/components/HomepageFeatures/styles.module.css +11 -0
- package/docs/src/css/custom.css +30 -0
- package/docs/src/pages/index.js +43 -0
- package/docs/src/pages/index.module.css +23 -0
- package/docs/src/pages/markdown-page.md +7 -0
- 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 +1 -0
- package/docs/static/img/undraw_docusaurus_mountain.svg +171 -0
- package/docs/static/img/undraw_docusaurus_react.svg +170 -0
- package/docs/static/img/undraw_docusaurus_tree.svg +40 -0
- package/package.json +75 -0
- package/scripts/generate-chain/index.js +111 -0
- package/scripts/generate-lib/index.js +68 -0
- package/scripts/generate-test/index.js +111 -0
- package/scripts/generate-verblet/README.md +17 -0
- package/scripts/generate-verblet/index.js +110 -0
- package/scripts/run.sh +15 -0
- package/scripts/runner/index.js +30 -0
- package/scripts/simple-editor/README.md +34 -0
- package/scripts/simple-editor/index.js +68 -0
- package/scripts/summarize-files/index.js +46 -0
- package/src/chains/dismantle/dismantle.examples.js +0 -0
- package/src/chains/dismantle/index.examples.js +30 -0
- package/src/chains/dismantle/index.js +314 -0
- package/src/chains/dismantle/index.spec.js +33 -0
- package/src/chains/list/index.examples.js +72 -0
- package/src/chains/list/index.js +161 -0
- package/src/chains/list/index.spec.js +68 -0
- package/src/chains/list/schema.json +24 -0
- package/src/chains/questions/index.examples.js +68 -0
- package/src/chains/questions/index.js +136 -0
- package/src/chains/questions/index.spec.js +29 -0
- package/src/chains/scan-js/index.js +119 -0
- package/src/chains/sort/index.examples.js +40 -0
- package/src/chains/sort/index.js +113 -0
- package/src/chains/sort/index.spec.js +115 -0
- package/src/chains/summary-map/README.md +33 -0
- package/src/chains/summary-map/index.examples.js +57 -0
- package/src/chains/summary-map/index.js +208 -0
- package/src/chains/summary-map/index.spec.js +78 -0
- package/src/chains/test/index.js +118 -0
- package/src/chains/test-advice/index.js +36 -0
- package/src/constants/common.js +9 -0
- package/src/constants/messages.js +3 -0
- package/src/constants/openai.js +65 -0
- package/src/index.js +33 -0
- package/src/json-schemas/cars-test.json +11 -0
- package/src/json-schemas/index.js +18 -0
- package/src/json-schemas/intent.json +38 -0
- package/src/json-schemas/schema-dot-org-photograph.json +127 -0
- package/src/json-schemas/schema-dot-org-place.json +56 -0
- package/src/lib/any-signal/index.js +28 -0
- package/src/lib/chatgpt/index.js +143 -0
- package/src/lib/editor/index.js +31 -0
- package/src/lib/parse-js-parts/index.js +333 -0
- package/src/lib/parse-js-parts/index.spec.js +156 -0
- package/src/lib/path-aliases/index.js +39 -0
- package/src/lib/path-aliases/index.spec.js +70 -0
- package/src/lib/pave/index.js +34 -0
- package/src/lib/pave/index.spec.js +73 -0
- package/src/lib/prompt-cache/index.js +46 -0
- package/src/lib/retry/index.js +63 -0
- package/src/lib/retry/index.spec.js +86 -0
- package/src/lib/search-best-first/index.js +66 -0
- package/src/lib/search-js-files/code-features-property-definitions.json +123 -0
- package/src/lib/search-js-files/index.examples.js +22 -0
- package/src/lib/search-js-files/index.js +158 -0
- package/src/lib/search-js-files/index.spec.js +34 -0
- package/src/lib/search-js-files/scan-file.js +253 -0
- package/src/lib/shorten-text/index.js +30 -0
- package/src/lib/shorten-text/index.spec.js +68 -0
- package/src/lib/strip-numeric/index.js +5 -0
- package/src/lib/strip-response/index.js +35 -0
- package/src/lib/timed-abort-controller/index.js +41 -0
- package/src/lib/to-bool/index.js +8 -0
- package/src/lib/to-enum/index.js +14 -0
- package/src/lib/to-number/index.js +12 -0
- package/src/lib/to-number-with-units/index.js +51 -0
- package/src/lib/transcribe/index.js +61 -0
- package/src/prompts/README.md +15 -0
- package/src/prompts/as-enum.js +5 -0
- package/src/prompts/as-json-schema.js +9 -0
- package/src/prompts/as-object-with-schema.js +31 -0
- package/src/prompts/as-schema-org-text.js +17 -0
- package/src/prompts/as-schema-org-type.js +1 -0
- package/src/prompts/blog-post.js +7 -0
- package/src/prompts/code-features.js +28 -0
- package/src/prompts/constants.js +101 -0
- package/src/prompts/features-json-schema.js +27 -0
- package/src/prompts/generate-collection.js +26 -0
- package/src/prompts/generate-list.js +48 -0
- package/src/prompts/generate-questions.js +19 -0
- package/src/prompts/index.js +20 -0
- package/src/prompts/intent.js +66 -0
- package/src/prompts/output-succinct-names.js +3 -0
- package/src/prompts/select-from-threshold.js +18 -0
- package/src/prompts/sort.js +35 -0
- package/src/prompts/style.js +41 -0
- package/src/prompts/summarize.js +13 -0
- package/src/prompts/token-budget.js +3 -0
- package/src/prompts/wrap-list.js +14 -0
- package/src/prompts/wrap-variable.js +36 -0
- package/src/services/llm-model/index.js +114 -0
- package/src/services/llm-model/model.js +21 -0
- package/src/services/redis/index.js +84 -0
- package/src/verblets/auto/index.examples.js +28 -0
- package/src/verblets/auto/index.js +28 -0
- package/src/verblets/auto/index.spec.js +34 -0
- package/src/verblets/bool/index.examples.js +28 -0
- package/src/verblets/bool/index.js +28 -0
- package/src/verblets/bool/index.schema.json +14 -0
- package/src/verblets/bool/index.spec.js +35 -0
- package/src/verblets/enum/index.examples.js +33 -0
- package/src/verblets/enum/index.js +15 -0
- package/src/verblets/enum/index.spec.js +35 -0
- package/src/verblets/intent/index.examples.js +51 -0
- package/src/verblets/intent/index.js +72 -0
- package/src/verblets/intent/index.spec.js +31 -0
- package/src/verblets/number/index.examples.js +33 -0
- package/src/verblets/number/index.js +22 -0
- package/src/verblets/number/index.spec.js +35 -0
- package/src/verblets/number-with-units/index.examples.js +34 -0
- package/src/verblets/number-with-units/index.js +19 -0
- package/src/verblets/number-with-units/index.spec.js +46 -0
- package/src/verblets/schema-org/index.examples.js +56 -0
- package/src/verblets/schema-org/index.js +8 -0
- package/src/verblets/schema-org/index.spec.js +39 -0
- package/src/verblets/to-object/README.md +38 -0
- package/src/verblets/to-object/index.examples.js +29 -0
- package/src/verblets/to-object/index.js +136 -0
- package/src/verblets/to-object/index.spec.js +74 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import numberWithUnits from './index.js';
|
|
4
|
+
|
|
5
|
+
vi.mock('../../lib/chatgpt/index.js', () => ({
|
|
6
|
+
default: vi.fn().mockImplementation((text) => {
|
|
7
|
+
if (/Everest/.test(text)) {
|
|
8
|
+
return '{ "value": 29029, "unit": "feet" }';
|
|
9
|
+
}
|
|
10
|
+
return 'undefined';
|
|
11
|
+
}),
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
const examples = [
|
|
15
|
+
{
|
|
16
|
+
name: 'Basic usage',
|
|
17
|
+
inputs: { text: 'What is the height of Everest in feet' },
|
|
18
|
+
want: { value: 29029, unit: 'feet' },
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: 'Unanswerable question',
|
|
22
|
+
inputs: { text: 'What is my age in years' },
|
|
23
|
+
want: { typeOfValue: 'undefined', typeOfUnit: 'undefined' },
|
|
24
|
+
},
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
describe('Number with units verblet', () => {
|
|
28
|
+
examples.forEach((example) => {
|
|
29
|
+
it(example.name, async () => {
|
|
30
|
+
const result = await numberWithUnits(example.inputs.text);
|
|
31
|
+
if (example.want.value) {
|
|
32
|
+
expect(result?.value).toStrictEqual(example.want.value);
|
|
33
|
+
}
|
|
34
|
+
if (example.want.typeOfValue) {
|
|
35
|
+
expect(typeof result?.value).toStrictEqual(example.want.typeOfValue);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (example.want.unit) {
|
|
39
|
+
expect(result?.unit).toStrictEqual(example.want.unit);
|
|
40
|
+
}
|
|
41
|
+
if (example.want.typeOfValue) {
|
|
42
|
+
expect(typeof result?.value).toStrictEqual(example.want.typeOfValue);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import Ajv from 'ajv';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import { describe, expect, it } from 'vitest';
|
|
4
|
+
|
|
5
|
+
import { longTestTimeout } from '../../constants/common.js';
|
|
6
|
+
import schemaOrg from './index.js';
|
|
7
|
+
|
|
8
|
+
const resultSchemaWith = (type) => async () => {
|
|
9
|
+
return JSON.parse(
|
|
10
|
+
await fs.readFile(
|
|
11
|
+
`./src/json-schemas/schema-dot-org-${type.toLowerCase()}.json`
|
|
12
|
+
)
|
|
13
|
+
);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const examples = [
|
|
17
|
+
{
|
|
18
|
+
inputs: { text: 'Kyoto (location)' },
|
|
19
|
+
want: { resultSchema: resultSchemaWith('Place') },
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
inputs: { text: 'Kyoto (location)', type: 'Photograph' },
|
|
23
|
+
want: { resultSchema: resultSchemaWith('Photograph') },
|
|
24
|
+
},
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
describe('Schema.org verblet', () => {
|
|
28
|
+
examples.forEach((example) => {
|
|
29
|
+
const typeDisplay = example.inputs.type ? ` - ${example.inputs.type}` : '';
|
|
30
|
+
it(
|
|
31
|
+
`${example.inputs.text}${typeDisplay}`,
|
|
32
|
+
async () => {
|
|
33
|
+
const result = await schemaOrg(
|
|
34
|
+
example.inputs.text,
|
|
35
|
+
example.inputs.type
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
if (example.want.resultSchema) {
|
|
39
|
+
const schema = await example.want.resultSchema();
|
|
40
|
+
const ajv = new Ajv();
|
|
41
|
+
const validate = ajv.compile(schema);
|
|
42
|
+
|
|
43
|
+
const isValid = validate(result);
|
|
44
|
+
if (!isValid) {
|
|
45
|
+
console.error('Validation errors:');
|
|
46
|
+
console.error(JSON.stringify(validate.errors, null, 2));
|
|
47
|
+
console.error('Returned result:');
|
|
48
|
+
console.error(JSON.stringify(result, null, 2));
|
|
49
|
+
}
|
|
50
|
+
expect(isValid).toStrictEqual(true);
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
longTestTimeout
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import chatGPT from '../../lib/chatgpt/index.js';
|
|
2
|
+
import stripResponse from '../../lib/strip-response/index.js';
|
|
3
|
+
import { asSchemaOrgText } from '../../prompts/index.js';
|
|
4
|
+
import toObject from '../to-object/index.js';
|
|
5
|
+
|
|
6
|
+
export default async (text, type) => {
|
|
7
|
+
return toObject(stripResponse(await chatGPT(asSchemaOrgText(text, type))));
|
|
8
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import schemaOrg from './index.js';
|
|
4
|
+
|
|
5
|
+
vi.mock('../../lib/chatgpt/index.js', () => ({
|
|
6
|
+
default: vi.fn().mockImplementation((text) => {
|
|
7
|
+
if (/Kyoto \(location\)/.test(text)) {
|
|
8
|
+
// Nothing is done with the result
|
|
9
|
+
// so returning something complicated only introduces complexity
|
|
10
|
+
return '{}';
|
|
11
|
+
}
|
|
12
|
+
return 'undefined';
|
|
13
|
+
}),
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
const examples = [
|
|
17
|
+
{
|
|
18
|
+
name: 'Basic usage',
|
|
19
|
+
inputs: { text: 'Kyoto (location)' },
|
|
20
|
+
want: { typeOfResult: 'object' },
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'Basic usage',
|
|
24
|
+
inputs: { text: 'Kyoto (location)', type: 'Photo' },
|
|
25
|
+
want: { typeOfResult: 'object' },
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
describe('Schema.org verblet', () => {
|
|
30
|
+
examples.forEach((example) => {
|
|
31
|
+
it(example.name, async () => {
|
|
32
|
+
const result = await schemaOrg(example.inputs.text);
|
|
33
|
+
|
|
34
|
+
if (example.want.typeOfResult) {
|
|
35
|
+
expect(typeof result).toStrictEqual(example.want.typeOfResult);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# to-object - JSON repair and validation
|
|
2
|
+
|
|
3
|
+
This module is designed to take JSON results from ChatGPT calls, which can often be imperfect or malformed, and repair them to create valid JSON. If a JSON Schema is provided, the module also validates the repaired JSON against it. This process ensures that the JSON data is both well-formed and conforms to the expected structure.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Automatic repair of malformed JSON data returned by ChatGPT.
|
|
8
|
+
- Validation of repaired JSON data against provided JSON Schemas using the [Ajv](https://ajv.js.org/) library.
|
|
9
|
+
- Recursive repair attempts through internal calls to ChatGPT for particularly stubborn cases.
|
|
10
|
+
- Detailed error handling and validation messages
|
|
11
|
+
- Custom ValidationError class for understanding where the returned JSON fails to meet the schema.
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
This module is part of a larger project and is not designed to be installed standalone. However, you can copy the module into your own project and install the necessary dependencies via npm or yarn:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install ajv
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
The primary function exported by this module takes two arguments: a text string to be parsed into JSON, and an optional schema object which describes the expected format of the JSON data.
|
|
24
|
+
|
|
25
|
+
Here is an example of how to use the module:
|
|
26
|
+
```javascript
|
|
27
|
+
import toObject from './path/to/module';
|
|
28
|
+
|
|
29
|
+
const text = '...'; // JSON string returned from a ChatGPT call
|
|
30
|
+
const schema = { ... }; // Optional JSON Schema
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const result = await toObject(text, schema);
|
|
34
|
+
console.error(result); // Repaired and validated JSON
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.error(error); // Validation message if JSON doesn't meet the schema
|
|
37
|
+
}
|
|
38
|
+
```
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import toObject from './index.js';
|
|
4
|
+
import chatGPT from '../../lib/chatgpt/index.js';
|
|
5
|
+
import { longTestTimeout } from '../../constants/common.js';
|
|
6
|
+
|
|
7
|
+
const examples = [
|
|
8
|
+
{
|
|
9
|
+
inputs: { text: 'Describe SpaceX Starship' },
|
|
10
|
+
want: { typeOfResult: 'object' },
|
|
11
|
+
},
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
describe('To object verblet', () => {
|
|
15
|
+
examples.forEach((example) => {
|
|
16
|
+
it(
|
|
17
|
+
example.inputs.text,
|
|
18
|
+
async () => {
|
|
19
|
+
const chatGPTResult = await chatGPT(example.inputs.text);
|
|
20
|
+
const result = await toObject(chatGPTResult);
|
|
21
|
+
|
|
22
|
+
if (example.want.typeOfResult) {
|
|
23
|
+
expect(typeof result).toStrictEqual(example.want.typeOfResult);
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
longTestTimeout
|
|
27
|
+
);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import Ajv from 'ajv';
|
|
2
|
+
|
|
3
|
+
import { debugToObject } from '../../constants/common.js';
|
|
4
|
+
import { retryJSONParse } from '../../constants/messages.js';
|
|
5
|
+
import chatGPT from '../../lib/chatgpt/index.js';
|
|
6
|
+
import stripResponse from '../../lib/strip-response/index.js';
|
|
7
|
+
import {
|
|
8
|
+
constants as promptConstants,
|
|
9
|
+
wrapVariable,
|
|
10
|
+
} from '../../prompts/index.js';
|
|
11
|
+
|
|
12
|
+
const { contentIsSchema, contentToJSON, onlyJSON, shapeAsJSON } =
|
|
13
|
+
promptConstants;
|
|
14
|
+
|
|
15
|
+
class ValidationError extends Error {
|
|
16
|
+
constructor(message, details) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = 'ValidationError';
|
|
19
|
+
this.details = details;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function buildJsonPrompt(text, schema, errors) {
|
|
24
|
+
let errorsDisplay = '';
|
|
25
|
+
if (errors?.length) {
|
|
26
|
+
errorsDisplay = wrapVariable(JSON.stringify(errors) ?? '', {
|
|
27
|
+
tag: 'json-schema-errors--do-not-output',
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let schemaPart = '';
|
|
32
|
+
if (schema) {
|
|
33
|
+
schemaPart = `${contentIsSchema} ${wrapVariable(JSON.stringify(schema), {
|
|
34
|
+
tag: 'json-schema--do-not-output',
|
|
35
|
+
})}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return `${onlyJSON} ${shapeAsJSON}
|
|
39
|
+
|
|
40
|
+
${schemaPart}
|
|
41
|
+
|
|
42
|
+
${errorsDisplay}
|
|
43
|
+
|
|
44
|
+
${contentToJSON} ${wrapVariable(stripResponse(text), { tag: 'content' })}
|
|
45
|
+
|
|
46
|
+
${onlyJSON}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export default async (text, schema) => {
|
|
50
|
+
let prompt;
|
|
51
|
+
let result;
|
|
52
|
+
let response = text;
|
|
53
|
+
let errorDetails;
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
result = JSON.parse(stripResponse(response));
|
|
57
|
+
if (schema) {
|
|
58
|
+
const ajv = new Ajv();
|
|
59
|
+
const validate = ajv.compile(schema);
|
|
60
|
+
const isValid = validate(result);
|
|
61
|
+
|
|
62
|
+
if (!isValid) {
|
|
63
|
+
throw new ValidationError('Ajv validation failed', validate.errors);
|
|
64
|
+
}
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
} catch (error) {
|
|
69
|
+
errorDetails = error.details;
|
|
70
|
+
if (debugToObject) {
|
|
71
|
+
console.error(`Parse JSON [error]: ${error.message} ${retryJSONParse}`);
|
|
72
|
+
console.error('<prompt attempt=1 value="unknown" />');
|
|
73
|
+
console.error('<response>');
|
|
74
|
+
console.error(stripResponse(response));
|
|
75
|
+
console.error('</response>');
|
|
76
|
+
console.error('<error>');
|
|
77
|
+
console.error(error);
|
|
78
|
+
console.error('</error>');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
prompt = buildJsonPrompt(response, schema, errorDetails);
|
|
84
|
+
response = await chatGPT(prompt, {
|
|
85
|
+
modelOptions: {
|
|
86
|
+
modelName: 'gpt35Turbo',
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
result = JSON.parse(stripResponse(response));
|
|
90
|
+
|
|
91
|
+
if (schema) {
|
|
92
|
+
const ajv = new Ajv();
|
|
93
|
+
const validate = ajv.compile(schema);
|
|
94
|
+
const isValid = validate(result);
|
|
95
|
+
|
|
96
|
+
if (!isValid) {
|
|
97
|
+
throw new ValidationError('Ajv validation failed', validate.errors);
|
|
98
|
+
}
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
101
|
+
} catch (error) {
|
|
102
|
+
errorDetails = error.details;
|
|
103
|
+
if (debugToObject) {
|
|
104
|
+
console.error(`Parse JSON [error]: ${error.message} ${retryJSONParse}`);
|
|
105
|
+
console.error('<prompt attempt=2>');
|
|
106
|
+
console.error(prompt);
|
|
107
|
+
console.error('</prompt>');
|
|
108
|
+
console.error('<response>');
|
|
109
|
+
console.error(stripResponse(response));
|
|
110
|
+
console.error('</response>');
|
|
111
|
+
console.error('<error>');
|
|
112
|
+
console.error(error);
|
|
113
|
+
console.error('</error>');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
prompt = buildJsonPrompt(response, schema, errorDetails);
|
|
117
|
+
response = await chatGPT(prompt, {
|
|
118
|
+
modelOptions: {
|
|
119
|
+
modelName: 'gpt35Turbo',
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
result = JSON.parse(stripResponse(response));
|
|
123
|
+
|
|
124
|
+
if (debugToObject) {
|
|
125
|
+
console.error(`Parse JSON [error]: ${error.message} ${retryJSONParse}`);
|
|
126
|
+
console.error('<prompt attempt=3>');
|
|
127
|
+
console.error(prompt);
|
|
128
|
+
console.error('</prompt>');
|
|
129
|
+
console.error('<response>');
|
|
130
|
+
console.error(stripResponse(response));
|
|
131
|
+
console.error('</response>');
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return result;
|
|
136
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import toObject from './index.js';
|
|
4
|
+
|
|
5
|
+
vi.mock('../../lib/chatgpt/index.js', () => ({
|
|
6
|
+
default: vi.fn().mockImplementation((text) => {
|
|
7
|
+
if (/test/.test(text)) {
|
|
8
|
+
return '{}';
|
|
9
|
+
}
|
|
10
|
+
if (/valid-schema/.test(text)) {
|
|
11
|
+
return '{"key": "value"}';
|
|
12
|
+
}
|
|
13
|
+
if (/invalid-schema/.test(text)) {
|
|
14
|
+
return '{"key": "value", "extra": "unexpected"}';
|
|
15
|
+
}
|
|
16
|
+
return 'undefined';
|
|
17
|
+
}),
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
const examples = [
|
|
21
|
+
{
|
|
22
|
+
name: 'Basic usage',
|
|
23
|
+
inputs: { text: 'test' },
|
|
24
|
+
want: { typeOfResult: 'object' },
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: 'Valid schema',
|
|
28
|
+
inputs: {
|
|
29
|
+
text: 'valid-schema',
|
|
30
|
+
schema: {
|
|
31
|
+
type: 'object',
|
|
32
|
+
properties: { key: { type: 'string' } },
|
|
33
|
+
required: ['key'],
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
want: { typeOfResult: 'object' },
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'Invalid schema',
|
|
40
|
+
inputs: {
|
|
41
|
+
text: 'invalid-schema',
|
|
42
|
+
schema: {
|
|
43
|
+
type: 'object',
|
|
44
|
+
properties: { key: { type: 'string' } },
|
|
45
|
+
additionalProperties: false,
|
|
46
|
+
required: ['key'],
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
wantError: true,
|
|
50
|
+
},
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
describe('To object verblet', () => {
|
|
54
|
+
examples.forEach((example) => {
|
|
55
|
+
it(example.name, async () => {
|
|
56
|
+
try {
|
|
57
|
+
const result = await toObject(
|
|
58
|
+
example.inputs.text,
|
|
59
|
+
example.inputs.schema
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
if (example.want.typeOfResult) {
|
|
63
|
+
expect(typeof result).toStrictEqual(example.want.typeOfResult);
|
|
64
|
+
}
|
|
65
|
+
} catch (error) {
|
|
66
|
+
if (example.wantError) {
|
|
67
|
+
expect(error).toBeInstanceOf(Error);
|
|
68
|
+
} else {
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
});
|