diff --git a/src/utils.ts b/src/utils.ts index d8aec87..b0b58c3 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -50,7 +50,7 @@ export function isValidTaskIdentifier(id: string): boolean { * @param maxLength - maximum length of each chunk * @returns array of chunks */ -export const chunkString = (text: string, maxLength: number) => { +export function chunkString(text: string, maxLength: number): string[] { const words = text.split(' ') const chunks: string[] = [] let chunk = '' @@ -74,3 +74,41 @@ export const chunkString = (text: string, maxLength: number) => { return chunks } + +/** + * Stringifies a JSON value for use in an LLM prompt. + * + * @param json - JSON value to stringify + * @returns stringified value with all double quotes around object keys removed + */ +export function stringifyForModel(json: types.JsonValue): string { + const UNIQUE_PREFIX = defaultIDGeneratorFn() + return ( + JSON.stringify(json, replacer) + // Remove all double quotes around keys: + .replace(new RegExp('"' + UNIQUE_PREFIX + '(.*?)"', 'g'), '$1') + ) + + /** + * Replacer function prefixing all keys with a unique identifier. + */ + function replacer(_: string, value: any) { + if (value && typeof value === 'object') { + if (Array.isArray(value)) { + return value + } + + const replacement = {} + + for (const k in value) { + if (Object.hasOwnProperty.call(value, k)) { + replacement[UNIQUE_PREFIX + k] = value[k] + } + } + + return replacement + } + + return value + } +} diff --git a/test/utils.test.ts b/test/utils.test.ts index e6021e5..d9b13ec 100644 --- a/test/utils.test.ts +++ b/test/utils.test.ts @@ -6,7 +6,8 @@ import { extractJSONArrayFromString, extractJSONObjectFromString, isValidTaskIdentifier, - sleep + sleep, + stringifyForModel } from '@/utils' test('isValidTaskIdentifier - valid', async (t) => { @@ -75,3 +76,51 @@ test('chunkString should split string into chunks', (t) => { 'function.' ]) }) + +test('stringifyForModel should stringify JSON values correctly', (t) => { + const input = { a: 1, b: 2 } + const expectedOutput = '{a:1,b:2}' + const actualOutput = stringifyForModel(input) + t.is(actualOutput, expectedOutput) +}) + +test('stringifyForModel should stringify primitive values correctly', (t) => { + const input = true + const expectedOutput = 'true' + const actualOutput = stringifyForModel(input) + t.is(actualOutput, expectedOutput) + + const input2 = 1 + const expectedOutput2 = '1' + const actualOutput2 = stringifyForModel(input2) + t.is(actualOutput2, expectedOutput2) + + const input3 = 'foo' + const expectedOutput3 = '"foo"' + const actualOutput3 = stringifyForModel(input3) + t.is(actualOutput3, expectedOutput3) +}) + +test('stringifyForModel should stringify nested objects correctly', (t) => { + const input = { a: 1, b: { c: 3, d: 4 } } + const expectedOutput = '{a:1,b:{c:3,d:4}}' + const actualOutput = stringifyForModel(input) + + t.is(actualOutput, expectedOutput) +}) + +test('stringifyForModel should stringify arrays correctly', (t) => { + const input = { a: 'Hello World!', b: [2, 3] } + const expectedOutput = '{a:"Hello World!",b:[2,3]}' + const actualOutput = stringifyForModel(input) + + t.is(actualOutput, expectedOutput) +}) + +test('stringifyForModel should stringify objects with null values correctly', (t) => { + const input = { a: 'baz', b: null } + const expectedOutput = '{a:"baz",b:null}' + const actualOutput = stringifyForModel(input) + + t.is(actualOutput, expectedOutput) +})