{"_id":"@k-apps.io/llm-dsl","_rev":"27-f5be4f8b84164fb4f1001882423d3385","time":{"created":"2023-11-02T04:58:53.338Z","1.0.0":"2023-11-02T03:27:48.146Z","modified":"2024-03-24T19:23:15.578Z","1.0.1":"2023-11-02T04:58:53.616Z","1.0.2":"2023-11-02T05:14:14.888Z","1.0.3":"2023-11-03T02:46:44.805Z","1.0.4":"2023-11-03T02:58:04.059Z","1.0.5":"2023-11-03T03:37:46.503Z","1.0.6":"2023-11-03T04:26:15.548Z","1.0.7":"2023-11-04T01:20:00.573Z","1.0.8":"2023-11-04T01:29:13.848Z","1.0.9":"2023-11-05T01:40:33.666Z","1.0.10":"2023-11-06T00:34:33.112Z","1.0.11":"2023-11-07T02:57:42.843Z","1.0.12":"2023-11-10T01:52:24.443Z","1.2.1-beta.0":"2023-11-12T19:13:32.130Z","1.2.1-beta.1":"2023-11-13T04:43:21.806Z","1.2.1-beta.2":"2023-11-20T03:25:45.013Z","1.2.1-beta.3":"2023-11-21T17:23:14.604Z","1.2.1-beta.4":"2023-11-21T21:05:55.848Z","1.2.1-beta.5":"2023-11-21T21:28:35.646Z","1.2.1-beta.6":"2023-11-21T22:15:49.235Z","1.2.1-beta.7":"2024-01-09T12:26:43.133Z","1.2.1-beta.8":"2024-01-09T13:33:48.661Z","1.2.1-beta.9":"2024-01-09T13:34:59.797Z","1.2.1-beta.10":"2024-01-10T12:48:03.834Z","1.2.1-beta.11":"2024-01-10T12:54:31.895Z","1.3.0":"2024-03-24T19:23:14.929Z"},"name":"@k-apps.io/llm-dsl","dist-tags":{"latest":"1.3.0","beta":"1.2.1-beta.11"},"versions":{"1.0.1":{"name":"@k-apps.io/llm-dsl","version":"1.0.1","description":"a DSL for Large Language Models (LLM)","main":"src/index.ts","scripts":{"test":"jest","build":"tsc","deploy":"npm run build && npm publish --access public"},"repository":{"type":"git","url":"git+https://github.com/k-apps-io/llm-dsl.git"},"keywords":["LLM","chatgpt","openai"],"author":{"name":"@imnotakopp"},"license":"ISC","bugs":{"url":"https://github.com/k-apps-io/llm-dsl/issues"},"homepage":"https://github.com/k-apps-io/llm-dsl#readme","devDependencies":{"@types/jest":"^29.5.1","@types/lodash":"^4.14.200","@types/uuid":"^9.0.6","dotenv":"^16.3.1","jest":"^29.5.0","openai":"^4.14.2","tiktoken":"^1.0.10","ts-jest":"^29.1.0","ts-node":"^10.9.1","typescript":"^5.2.2"},"dependencies":{"ebnf":"^1.9.1","lodash":"^4.17.21","uuid":"^9.0.1"},"gitHead":"d737c48da7072d0008a00fd3fea2bfa07269d64d","_id":"@k-apps.io/llm-dsl@1.0.1","_nodeVersion":"18.13.0","_npmVersion":"9.2.0","dist":{"integrity":"sha512-LdOHZagZVl5GfLWcguXvr2NV18ADtjs3yPvh+l4K7JbfTuxjFJrxKUriObKqnWLrb+fdYt2iAlRgVX4OLEjdhA==","shasum":"8b4b61fdcec9ba1990e34ef094a25647fcf1ea16","tarball":"https://registry.npmjs.org/@k-apps.io/llm-dsl/-/llm-dsl-1.0.1.tgz","fileCount":5,"unpackedSize":13843,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDmoF568viDMKdHKDQOYan6NYJkm1/iwR1+KUK4SjPh9AIgPvWBZdsfbLO2QUHJth4QgiTNJd/Ddf8ls6wF2AF+tJ0="}]},"_npmUser":{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"},"directories":{},"maintainers":[{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/llm-dsl_1.0.1_1698901133395_0.9684468941837465"},"_hasShrinkwrap":false},"1.0.2":{"name":"@k-apps.io/llm-dsl","version":"1.0.2","description":"a DSL for Large Language Models (LLM)","main":"src/index.ts","scripts":{"test":"jest","build":"tsc","deploy":"npm run build && npm publish --access public"},"repository":{"type":"git","url":"git+https://github.com/k-apps-io/llm-dsl.git"},"keywords":["LLM","chatgpt","openai"],"author":{"name":"@imnotakopp"},"license":"ISC","bugs":{"url":"https://github.com/k-apps-io/llm-dsl/issues"},"homepage":"https://github.com/k-apps-io/llm-dsl#readme","devDependencies":{"@types/jest":"^29.5.1","@types/lodash":"^4.14.200","@types/uuid":"^9.0.6","dotenv":"^16.3.1","jest":"^29.5.0","openai":"^4.14.2","tiktoken":"^1.0.10","ts-jest":"^29.1.0","ts-node":"^10.9.1","typescript":"^5.2.2"},"dependencies":{"ebnf":"^1.9.1","lodash":"^4.17.21","uuid":"^9.0.1"},"gitHead":"d737c48da7072d0008a00fd3fea2bfa07269d64d","_id":"@k-apps.io/llm-dsl@1.0.2","_nodeVersion":"18.13.0","_npmVersion":"9.2.0","dist":{"integrity":"sha512-ij5qLsSZD0kp47Fmr3g+WxyTe9H8lFnnrBrRoAL6F5wnnj4CwCCDMhLM6tXB8L1NmOVJwKqiagsuyP/K4/Zp9A==","shasum":"d98a0d52471d041d386a0703c65aae80151d5106","tarball":"https://registry.npmjs.org/@k-apps.io/llm-dsl/-/llm-dsl-1.0.2.tgz","fileCount":5,"unpackedSize":13847,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHRiX4LHvtHJ/WF6Iogy8NwKIpRwatMVWhyVuYICkZE2AiAaTdIX2fKt36kh6dg1a1azVdIGR5k04nl38tqYw5DK7Q=="}]},"_npmUser":{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"},"directories":{},"maintainers":[{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/llm-dsl_1.0.2_1698902054680_0.9916204123957211"},"_hasShrinkwrap":false},"1.0.3":{"name":"@k-apps.io/llm-dsl","version":"1.0.3","description":"a DSL for Large Language Models (LLM)","main":"dist/index.ts","scripts":{"test":"jest","build":"tsc","deploy":"npm run build && npm publish --access public"},"repository":{"type":"git","url":"git+https://github.com/k-apps-io/llm-dsl.git"},"keywords":["LLM","chatgpt","openai"],"author":{"name":"@imnotakopp"},"license":"ISC","bugs":{"url":"https://github.com/k-apps-io/llm-dsl/issues"},"homepage":"https://github.com/k-apps-io/llm-dsl#readme","devDependencies":{"@types/jest":"^29.5.1","@types/lodash":"^4.14.200","@types/uuid":"^9.0.6","dotenv":"^16.3.1","jest":"^29.5.0","openai":"^4.14.2","tiktoken":"^1.0.10","ts-jest":"^29.1.0","ts-node":"^10.9.1","typescript":"^5.2.2"},"dependencies":{"ebnf":"^1.9.1","lodash":"^4.17.21","uuid":"^9.0.1"},"types":"./dist/index.d.ts","gitHead":"03c39f935dfc08b58bd3522ed6abf8e54387fa2b","_id":"@k-apps.io/llm-dsl@1.0.3","_nodeVersion":"18.13.0","_npmVersion":"9.2.0","dist":{"integrity":"sha512-FaRG+bbhXshJ/os/Rkg6YqfSKgKsdTI/Ds2IXPL1gxP5WJXRAcDnr8OdHlLE7fO/EFWMK0OFJjLaPfZHJEDiGA==","shasum":"10886781df40e4674222f4b983ab13c494faf4df","tarball":"https://registry.npmjs.org/@k-apps.io/llm-dsl/-/llm-dsl-1.0.3.tgz","fileCount":4,"unpackedSize":13570,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDTMLk/pdYM++LdFcTbYaBytITzSL9XuuQ/oWV+BATx2gIhAONwRIwqq4wqd317rnIeZh2z3zvVuP1SdB8g5xo8sMRa"}]},"_npmUser":{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"},"directories":{},"maintainers":[{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/llm-dsl_1.0.3_1698979604574_0.8844478402138927"},"_hasShrinkwrap":false},"1.0.4":{"name":"@k-apps.io/llm-dsl","version":"1.0.4","description":"a DSL for Large Language Models (LLM)","main":"dist/index.ts","scripts":{"test":"jest","build":"tsc","deploy":"npm run build && npm publish --access public"},"repository":{"type":"git","url":"git+https://github.com/k-apps-io/llm-dsl.git"},"keywords":["LLM","chatgpt","openai"],"author":{"name":"@imnotakopp"},"license":"ISC","bugs":{"url":"https://github.com/k-apps-io/llm-dsl/issues"},"homepage":"https://github.com/k-apps-io/llm-dsl#readme","devDependencies":{"@types/jest":"^29.5.1","@types/lodash":"^4.14.200","@types/uuid":"^9.0.6","dotenv":"^16.3.1","jest":"^29.5.0","openai":"^4.14.2","tiktoken":"^1.0.10","ts-jest":"^29.1.0","ts-node":"^10.9.1","typescript":"^5.2.2"},"dependencies":{"ebnf":"^1.9.1","lodash":"^4.17.21","uuid":"^9.0.1"},"types":"./dist/index.d.ts","gitHead":"03c39f935dfc08b58bd3522ed6abf8e54387fa2b","_id":"@k-apps.io/llm-dsl@1.0.4","_nodeVersion":"18.13.0","_npmVersion":"9.2.0","dist":{"integrity":"sha512-LlXIIewPq2Op/gTQTzq15xydUbQtAbt/r3UUII1IXwxmeYK30CVwyjV8gJvLe5ox0pdwzrZWFvwdhaJCmTUX7w==","shasum":"c759d293585cf37a2d5b72d5cc031e804c2bf62f","tarball":"https://registry.npmjs.org/@k-apps.io/llm-dsl/-/llm-dsl-1.0.4.tgz","fileCount":14,"unpackedSize":39837,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFcsOnToUUJ8Amr5W9x+dgowhFgEVb6k9Swp60OfdjCgAiEAuOCVhH/XgbLdq3yF4l2DqGbKA9jmDONCrxMgkoA68QU="}]},"_npmUser":{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"},"directories":{},"maintainers":[{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/llm-dsl_1.0.4_1698980283887_0.307446775103688"},"_hasShrinkwrap":false},"1.0.5":{"name":"@k-apps.io/llm-dsl","version":"1.0.5","description":"a DSL for Large Language Models (LLM)","main":"dist/index.js","scripts":{"test":"jest","build":"tsc","deploy":"npm run build && npm publish --access public"},"repository":{"type":"git","url":"git+https://github.com/k-apps-io/llm-dsl.git"},"keywords":["LLM","chatgpt","openai"],"author":{"name":"@imnotakopp"},"license":"ISC","bugs":{"url":"https://github.com/k-apps-io/llm-dsl/issues"},"homepage":"https://github.com/k-apps-io/llm-dsl#readme","devDependencies":{"@types/jest":"^29.5.1","@types/lodash":"^4.14.200","@types/uuid":"^9.0.6","dotenv":"^16.3.1","jest":"^29.5.0","openai":"^4.14.2","tiktoken":"^1.0.10","ts-jest":"^29.1.0","ts-node":"^10.9.1","typescript":"^5.2.2"},"dependencies":{"ebnf":"^1.9.1","lodash":"^4.17.21","uuid":"^9.0.1"},"types":"./dist/index.d.ts","gitHead":"03c39f935dfc08b58bd3522ed6abf8e54387fa2b","_id":"@k-apps.io/llm-dsl@1.0.5","_nodeVersion":"18.13.0","_npmVersion":"9.2.0","dist":{"integrity":"sha512-TZOpLKjGaOZcPyhynV+XQIJaCA/198NFNvhBhSg6XtdthXU1YLK3PRfN19bI9LNy0suCaXo/YRL9ZrTRZITwdg==","shasum":"df2b182a0f80c70f72c93189cdcd536e1605fd11","tarball":"https://registry.npmjs.org/@k-apps.io/llm-dsl/-/llm-dsl-1.0.5.tgz","fileCount":14,"unpackedSize":39837,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD6TIPdlbiEjPCpZqanQDP92sAlcsCY2X/59q073evxZwIgDqJ6QMB/jEB6wv9jpxsbYkqVb6M45SLCR6qm1QhSx3k="}]},"_npmUser":{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"},"directories":{},"maintainers":[{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/llm-dsl_1.0.5_1698982666277_0.772514035841994"},"_hasShrinkwrap":false},"1.0.6":{"name":"@k-apps.io/llm-dsl","version":"1.0.6","description":"a DSL for Large Language Models (LLM)","main":"dist/index.js","scripts":{"test":"jest","build":"tsc","deploy":"npm run build && npm publish --access public"},"repository":{"type":"git","url":"git+https://github.com/k-apps-io/llm-dsl.git"},"keywords":["LLM","chatgpt","openai"],"author":{"name":"@imnotakopp"},"license":"ISC","bugs":{"url":"https://github.com/k-apps-io/llm-dsl/issues"},"homepage":"https://github.com/k-apps-io/llm-dsl#readme","devDependencies":{"@types/jest":"^29.5.1","@types/lodash":"^4.14.200","@types/uuid":"^9.0.6","dotenv":"^16.3.1","jest":"^29.5.0","openai":"^4.14.2","tiktoken":"^1.0.10","ts-jest":"^29.1.0","ts-node":"^10.9.1","typescript":"^5.2.2"},"dependencies":{"ebnf":"^1.9.1","lodash":"^4.17.21","uuid":"^9.0.1"},"types":"./dist/index.d.ts","gitHead":"03c39f935dfc08b58bd3522ed6abf8e54387fa2b","_id":"@k-apps.io/llm-dsl@1.0.6","_nodeVersion":"18.13.0","_npmVersion":"9.2.0","dist":{"integrity":"sha512-PInz8+mH035AOTAik4L2JdH/6jFo8klZ6IcupUFJ9E00Jhz6tqH6yKcJAxaUTl+PEzAsUw1Td6tpWwAmTZQrJg==","shasum":"86b005c113fd76b17aa5df7f80a654e58985d780","tarball":"https://registry.npmjs.org/@k-apps.io/llm-dsl/-/llm-dsl-1.0.6.tgz","fileCount":14,"unpackedSize":40165,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD2uPzUWo3NtNvgfZkEGry8ngSCOLA6awZr5CtaM7WmXgIhALFD1f4PNOCYEFCN2MzJQF9UxLQsdYRd1+POxHW8IBYd"}]},"_npmUser":{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"},"directories":{},"maintainers":[{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/llm-dsl_1.0.6_1698985575261_0.6126229143163531"},"_hasShrinkwrap":false},"1.0.7":{"name":"@k-apps.io/llm-dsl","version":"1.0.7","description":"a DSL for Large Language Models (LLM)","main":"dist/index.js","scripts":{"test":"jest","build":"tsc","deploy":"npm run build && npm publish --access public"},"repository":{"type":"git","url":"git+https://github.com/k-apps-io/llm-dsl.git"},"keywords":["LLM","chatgpt","openai"],"author":{"name":"@imnotakopp"},"license":"ISC","bugs":{"url":"https://github.com/k-apps-io/llm-dsl/issues"},"homepage":"https://github.com/k-apps-io/llm-dsl#readme","devDependencies":{"@types/jest":"^29.5.1","@types/lodash":"^4.14.200","@types/uuid":"^9.0.6","dotenv":"^16.3.1","jest":"^29.5.0","openai":"^4.14.2","tiktoken":"^1.0.10","ts-jest":"^29.1.0","ts-node":"^10.9.1","typescript":"^5.2.2"},"dependencies":{"ebnf":"^1.9.1","lodash":"^4.17.21","uuid":"^9.0.1"},"types":"./dist/index.d.ts","gitHead":"03c39f935dfc08b58bd3522ed6abf8e54387fa2b","_id":"@k-apps.io/llm-dsl@1.0.7","_nodeVersion":"18.13.0","_npmVersion":"9.2.0","dist":{"integrity":"sha512-qPGiEK2Dns/GYuOkH6GGKrbHhhTT02aQcwkOesyJunAvl6tS9NBoh/LCbBjDtQO0ZgupRvUVrCVH5hH7VQkSQA==","shasum":"036d2cd3619e9008ac37604a7decf49e5c4556ea","tarball":"https://registry.npmjs.org/@k-apps.io/llm-dsl/-/llm-dsl-1.0.7.tgz","fileCount":14,"unpackedSize":40608,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGekXODgV+QKFEtY4YqKR+sq7k7WCWbfb4blvGtWwBqWAiAYPXl0ctvwEUnNmOHJrL9FoOaCbzfsexuPfRCDyEAtOA=="}]},"_npmUser":{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"},"directories":{},"maintainers":[{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/llm-dsl_1.0.7_1699060800352_0.7707790742181455"},"_hasShrinkwrap":false},"1.0.8":{"name":"@k-apps.io/llm-dsl","version":"1.0.8","description":"a DSL for Large Language Models (LLM)","main":"dist/index.js","scripts":{"test":"jest","build":"tsc","deploy":"npm run build && npm publish --access public"},"repository":{"type":"git","url":"git+https://github.com/k-apps-io/llm-dsl.git"},"keywords":["LLM","chatgpt","openai"],"author":{"name":"@imnotakopp"},"license":"ISC","bugs":{"url":"https://github.com/k-apps-io/llm-dsl/issues"},"homepage":"https://github.com/k-apps-io/llm-dsl#readme","devDependencies":{"@types/jest":"^29.5.1","@types/lodash":"^4.14.200","@types/uuid":"^9.0.6","dotenv":"^16.3.1","jest":"^29.5.0","openai":"^4.14.2","tiktoken":"^1.0.10","ts-jest":"^29.1.0","ts-node":"^10.9.1","typescript":"^5.2.2"},"dependencies":{"ebnf":"^1.9.1","lodash":"^4.17.21","uuid":"^9.0.1"},"types":"./dist/index.d.ts","gitHead":"03c39f935dfc08b58bd3522ed6abf8e54387fa2b","_id":"@k-apps.io/llm-dsl@1.0.8","_nodeVersion":"18.13.0","_npmVersion":"9.2.0","dist":{"integrity":"sha512-5sF24X/y87+yfNxl+Del+LBtiqIGxJnavsR+yE5JKdo2zzCRg3sy+ZkNCZuDM3WzCAjSckKgpir7vWNT8v+fQQ==","shasum":"f827ea45dfbde6aa7e916fe2c4e2fb81aa38a42d","tarball":"https://registry.npmjs.org/@k-apps.io/llm-dsl/-/llm-dsl-1.0.8.tgz","fileCount":14,"unpackedSize":40872,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEr2IxNceRF9AzPe++j9qEmL14EM4q5OK/UmccfHifyfAiBsW4ygvBJPhQ/B9OEN4C2oHUdj+GQ+R+t0XVhPnUCdag=="}]},"_npmUser":{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"},"directories":{},"maintainers":[{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/llm-dsl_1.0.8_1699061353682_0.6624027940838224"},"_hasShrinkwrap":false},"1.0.9":{"name":"@k-apps.io/llm-dsl","version":"1.0.9","description":"a DSL for Large Language Models (LLM)","main":"dist/index.js","scripts":{"test":"jest","build":"tsc","deploy":"npm run build && npm publish --access public"},"repository":{"type":"git","url":"git+https://github.com/k-apps-io/llm-dsl.git"},"keywords":["LLM","chatgpt","openai"],"author":{"name":"@imnotakopp"},"license":"ISC","bugs":{"url":"https://github.com/k-apps-io/llm-dsl/issues"},"homepage":"https://github.com/k-apps-io/llm-dsl#readme","devDependencies":{"@types/jest":"^29.5.1","@types/lodash":"^4.14.200","@types/uuid":"^9.0.6","dotenv":"^16.3.1","jest":"^29.5.0","openai":"^4.14.2","tiktoken":"^1.0.10","ts-jest":"^29.1.0","ts-node":"^10.9.1","typescript":"^5.2.2"},"dependencies":{"ebnf":"^1.9.1","lodash":"^4.17.21","uuid":"^9.0.1"},"types":"./dist/index.d.ts","gitHead":"ad6bbbdda360868ca5610f8de5ada1ab4459e207","_id":"@k-apps.io/llm-dsl@1.0.9","_nodeVersion":"18.13.0","_npmVersion":"9.2.0","dist":{"integrity":"sha512-YSZxOT3Boee49fpW2SkWgEAAMIUmYyUQ1fmFsjANi539CCuLwQBUekcSiNai3ngHi7nOdWRi+xAro8mxXOU0PQ==","shasum":"bc3aec4ee734f8ff300dff2b8770f4914efb5c71","tarball":"https://registry.npmjs.org/@k-apps.io/llm-dsl/-/llm-dsl-1.0.9.tgz","fileCount":14,"unpackedSize":41691,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDNpTbX/kZ2V5gLxVdOOxu0guCwR+UakChcn5vwQZ/t1QIgB6tQ/AICfrDVklTcwdBV+HCDVCX0V71erV1VvmzPirY="}]},"_npmUser":{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"},"directories":{},"maintainers":[{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/llm-dsl_1.0.9_1699148433474_0.16364999887202858"},"_hasShrinkwrap":false},"1.0.10":{"name":"@k-apps.io/llm-dsl","version":"1.0.10","description":"a DSL for Large Language Models (LLM)","main":"dist/index.js","scripts":{"test":"jest","build":"tsc","deploy":"npm run build && npm publish --access public"},"repository":{"type":"git","url":"git+https://github.com/k-apps-io/llm-dsl.git"},"keywords":["LLM","chatgpt","openai"],"author":{"name":"@imnotakopp"},"license":"ISC","bugs":{"url":"https://github.com/k-apps-io/llm-dsl/issues"},"homepage":"https://github.com/k-apps-io/llm-dsl#readme","devDependencies":{"@k-apps.io/llm-dsl-chatgpt":"^1.0.7","@types/jest":"^29.5.1","@types/lodash":"^4.14.200","@types/uuid":"^9.0.6","dotenv":"^16.3.1","jest":"^29.5.0","openai":"^4.14.2","tiktoken":"^1.0.10","ts-jest":"^29.1.0","ts-node":"^10.9.1","typescript":"^5.2.2"},"dependencies":{"ebnf":"^1.9.1","lodash":"^4.17.21","uuid":"^9.0.1"},"types":"./dist/index.d.ts","gitHead":"ad6bbbdda360868ca5610f8de5ada1ab4459e207","_id":"@k-apps.io/llm-dsl@1.0.10","_nodeVersion":"18.13.0","_npmVersion":"9.2.0","dist":{"integrity":"sha512-B6T2w71/pxqgcbmT/aYeTpK1G+E1QCOH/eUyFN+Dg7vDBTXCqjOtKWw9+7TXjCJQz1lhqBr5XA92mAFpkeqkHA==","shasum":"b6299b9d54f3f00b1bfd704c01bb3d4fbf82b954","tarball":"https://registry.npmjs.org/@k-apps.io/llm-dsl/-/llm-dsl-1.0.10.tgz","fileCount":14,"unpackedSize":46201,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCjmfOqWL3+/E74aD8aDO+Pqq3jS8LOlr02B4It3PPpJgIhAKUkrmiR6XZlEvaVX8QcyJ75TXpl7UqclJNWAh9dn9lF"}]},"_npmUser":{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"},"directories":{},"maintainers":[{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/llm-dsl_1.0.10_1699230872838_0.474423938667313"},"_hasShrinkwrap":false},"1.0.11":{"name":"@k-apps.io/llm-dsl","version":"1.0.11","description":"a DSL for Large Language Models (LLM)","main":"dist/index.js","scripts":{"test":"jest","build":"tsc","deploy":"npm run build && npm publish --access public"},"repository":{"type":"git","url":"git+https://github.com/k-apps-io/llm-dsl.git"},"keywords":["LLM","chatgpt","openai"],"author":{"name":"@imnotakopp"},"license":"ISC","bugs":{"url":"https://github.com/k-apps-io/llm-dsl/issues"},"homepage":"https://github.com/k-apps-io/llm-dsl#readme","devDependencies":{"@k-apps.io/llm-dsl-chatgpt":"^1.0.7","@types/jest":"^29.5.1","@types/lodash":"^4.14.200","@types/uuid":"^9.0.6","dotenv":"^16.3.1","jest":"^29.5.0","openai":"^4.14.2","tiktoken":"^1.0.10","ts-jest":"^29.1.0","ts-node":"^10.9.1","typescript":"^5.2.2"},"dependencies":{"ebnf":"^1.9.1","lodash":"^4.17.21","uuid":"^9.0.1"},"types":"./dist/index.d.ts","gitHead":"7f4c40a5be61d67635d8e06190dd6b63d12918d6","_id":"@k-apps.io/llm-dsl@1.0.11","_nodeVersion":"18.13.0","_npmVersion":"9.2.0","dist":{"integrity":"sha512-/8hSaRN8lktWDq8ZAk40GwFzyZAVo+giNL0m/OaTssRmfkE7ykeIWfsiR7LxvzAcv53bbw7qjqATdbbbI9j4AA==","shasum":"2238d9c1dc5d18f56996664129c5eb011b2bb97b","tarball":"https://registry.npmjs.org/@k-apps.io/llm-dsl/-/llm-dsl-1.0.11.tgz","fileCount":14,"unpackedSize":48395,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEoWEkaBAfMMCD5Si5nupb87iLgw/BGfWq4nrpMfWRgyAiAdBBS3lygyR7K7rEHHM/SoJe9YL3fvV9vCXLLnTwzGSQ=="}]},"_npmUser":{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"},"directories":{},"maintainers":[{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/llm-dsl_1.0.11_1699325862598_0.8855879224898893"},"_hasShrinkwrap":false},"1.0.12":{"name":"@k-apps.io/llm-dsl","version":"1.0.12","description":"a DSL for Large Language Models (LLM)","main":"dist/index.js","scripts":{"test":"jest","build":"tsc","deploy":"npm run build && npm publish --access public"},"repository":{"type":"git","url":"git+https://github.com/k-apps-io/llm-dsl.git"},"keywords":["LLM","chatgpt","openai"],"author":{"name":"@imnotakopp"},"license":"ISC","bugs":{"url":"https://github.com/k-apps-io/llm-dsl/issues"},"homepage":"https://github.com/k-apps-io/llm-dsl#readme","devDependencies":{"@k-apps.io/llm-dsl-chatgpt":"^1.0.7","@types/jest":"^29.5.1","@types/lodash":"^4.14.200","@types/uuid":"^9.0.6","dotenv":"^16.3.1","jest":"^29.5.0","openai":"^4.14.2","tiktoken":"^1.0.10","ts-jest":"^29.1.0","ts-node":"^10.9.1","typescript":"^5.2.2"},"dependencies":{"ebnf":"^1.9.1","lodash":"^4.17.21","uuid":"^9.0.1"},"types":"./dist/index.d.ts","gitHead":"7f4c40a5be61d67635d8e06190dd6b63d12918d6","_id":"@k-apps.io/llm-dsl@1.0.12","_nodeVersion":"18.13.0","_npmVersion":"9.2.0","dist":{"integrity":"sha512-S/FMNvdMPU3nSjAYp+AnJ6I0Nd9Ut6aJwt7Rfptg8KfOihpjxSS8TQ6xRRtj3+XpF4MM/c31RS73gNaMdfN/nw==","shasum":"471fb153535b8626506a7c8eb1839c3d927efdf6","tarball":"https://registry.npmjs.org/@k-apps.io/llm-dsl/-/llm-dsl-1.0.12.tgz","fileCount":14,"unpackedSize":51119,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEnFQ56nIFjM3aTlY7IJ6MMP++gInlbySvqNDTwxBDGwAiA4A13XaC4MUw1yMznNi7PnQg2fCuJGgWwUas+xM1gtvQ=="}]},"_npmUser":{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"},"directories":{},"maintainers":[{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/llm-dsl_1.0.12_1699581144253_0.4477022205029997"},"_hasShrinkwrap":false},"1.2.1-beta.0":{"name":"@k-apps.io/llm-dsl","version":"1.2.1-beta.0","description":"a DSL for Large Language Models (LLM)","main":"dist/index.js","scripts":{"test":"jest","build":"tsc","deploy-prod":"npm run build && npm publish --access public","deploy":"npm run build && git add . && git commit -m \"Committing changes before beta deploy\" && npm version prerelease --preid=beta && npm publish --tag beta"},"repository":{"type":"git","url":"git+https://github.com/k-apps-io/llm-dsl.git"},"keywords":["LLM","chatgpt","openai"],"author":{"name":"@imnotakopp"},"license":"ISC","bugs":{"url":"https://github.com/k-apps-io/llm-dsl/issues"},"homepage":"https://github.com/k-apps-io/llm-dsl#readme","devDependencies":{"@k-apps.io/llm-dsl-chatgpt":"^1.0.7","@types/jest":"^29.5.1","@types/lodash":"^4.14.200","@types/uuid":"^9.0.6","dotenv":"^16.3.1","jest":"^29.5.0","openai":"^4.14.2","tiktoken":"^1.0.10","ts-jest":"^29.1.0","ts-node":"^10.9.1","typescript":"^5.2.2"},"dependencies":{"ebnf":"^1.9.1","lodash":"^4.17.21","uuid":"^9.0.1"},"types":"./dist/index.d.ts","readme":"# @k-apps.io/llm-dsl\nthis is a DSL for interacting with a LLM, chaining commands together to formulate robust tasks\n\n# Installation\n\n```shell\nnpm install @k-apps.io/llm-dsl\n```\n\npair this with an LLM implementation such as\n\n[@k-apps.io/llm-dsl-chatgpt](https://www.npmjs.com/package/@k-apps.io/llm-dsl-chatgpt)\n\n# Usage\n\n### Hello World\n```javascript\nimport { LLM, DSL, LocalStorage, Rules, CODE_BLOCK_RULE } from \"@k-apps.io/llm-dsl\";\nimport { ChatGPT, Options } from \"@k-apps.io/llm-dsl-chatgpt\";\n\nrequire( \"dotenv\" ).config();\n\nconst main = () => {\n\n  const LLM = new ChatGPT({});\n\n  type Context = string[];\n\n  const chat = new DSL<Options, Context>( {\n    llm: LLM,\n    storage: LocalStorage,\n    options: {\n      model: \"gpt-4\"\n    }\n  } );\n\n  chat\n    .rule( CODE_BLOCK_RULE )\n    .prompt( {\n      message: \"Hello!\"\n    } )\n    .prompt( {\n      message: 'send me 3 progamming languages as a JSON object e.g ```json [\"lang1\", \"lang2\", \"langN\"]```',\n    } )\n    .expect( ( response ) => new Promise<void>( ( resolve, reject ) => {\n      // setting expectations for the response\n      if ( response.codeBlocks === undefined ) {\n        reject( \"expecting a json code block\" );\n      } else if ( response.codeBlocks.length > 1 ) {\n        reject( \"only 1 code block was requested\" );\n      } else {\n        const block = response.codeBlocks[ 0 ];\n        if ( block.lang !== \"json\" ) {\n          reject( `expecting a json code block, found '${ block.lang }' instead` );\n        } else {\n          const data = JSON.parse( block.code );\n          // setting the context in this command makes it available hereafter\n          chat.context = data;\n          resolve();\n        }\n      }\n    } ) )\n    .promptForEach( ( languages: Context ) => {\n      // using this context I can send more prompts\n      return languages.map( language => {\n        const message = `write me a hello world progamming in ${ language }`;\n        return { message: message };\n      } );\n    } )\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then( () => {\n      console.log( \"done\" );\n    } )\n    .catch( error => {\n      console.error( error );\n    } );\n}\n```\n\n# Rules\na `rule` represents a specific instruction for the LLM and `you` to follow. Regardless where you define these in the chain of commands they will always be included at the top of the convesation with `Visibility.REQUIRED`\n\n```javascript\nimport { DSL, Rule } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    .rule({\n      name: \"Bob Uecker\",\n      requirement: \"respond in the style of Bob Uecker, the Famous Radio Broadcaster and not so Fammous American League Baseball Player\"\n    })\n    .prompt({\n      message: \"Uecke, give me a home run call\"\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content );\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Functions\n\nIn your DSL script, you can define custom `functions` that the Language Model (LLM) may call, and the results will be returned as new `Message` objects. This feature allows you to create custom logic and interactions within your conversation with the LLM.\n\n```javascript\nimport { DSL, Rule, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    .function<{ unit?: \"Fahrenheit\" | \"Celsius\"; }>( {\n      name: \"getTheCurrentWeather\",\n      description: \"helps the AI model determine the current weather\",\n      parameters: {\n        \"type\": \"object\",\n        \"properties\": {\n          \"unit\": {\n            \"type\": \"string\",\n            \"enum\": [ \"Fahrenheit\", \"Celsius\" ]\n          }\n        }\n      },\n      func: ( { unit = \"Fahrenheit\", context } ) => {\n        return new Promise( ( resolve, reject ) => {\n          const weather = {\n            temperature: unit === \"Fahrenheit\" ? 24 : 5,\n            unit: unit,\n            percipitation: \"it's snowing\"\n          };\n          resolve( {\n            message: `here is the current weather report \\`\\`\\`json ${ JSON.stringify( weather ) }\\`\\`\\``\n          } );\n        } );\n      }\n    } )\n    .rule( {\n      name: \"Temperature\",\n      requirement: \"all temperature readings must be in Celsius\"\n    } )\n    .prompt( {\n      message: \"what's the weather like today?\"\n    } )\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content );\n    } )\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Prompting\na `prompt` is the main form of interacting with the LLM, use this command to send a message and generate a response\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL<Options, string>({});\n  chat.context = \"what is something the bank has that could be used on the football field?\";\n  chat\n    .prompt({\n      message: \"Why did the football team go to the bank?\"\n    })\n    .prompt( context => {\n      // a prompt can also access the context of the chat via a `CommandFunction`\n      return {\n        message: `here is a hint: ${context}`\n      }\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Context\n\nthe `context` is a shared space that enables the seamless exchange of information between different commands and prompts. It's a vital tool for tasks that involve gathering, adjusting, or retrieving data across multiple interactions. The `context` ensures your conversation flows cohesively and empowers you to manage and transfer artifacts effortlessly.\n\nTo manage the `context`, you can use commands like [response](#response) and [expect](#expect) or call `setContext` directly\n\nTo use the `context`, checkout [prompt](#prompting), [promptForEach](#promptforeach) and [branchForEach](#branchforeach)\n\n\n## response\n`response` provides direct accesss the response of a prompt, it gives you control to continue the chain of commands by calling `resolve` or stop by calling `reject`\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL<Options, string>({});\n  chat\n    .prompt({\n      message: \"finish the sentence 'one does not'\"\n    })\n    .response( response => new Promise<void>(( resolve, reject ) => {\n      if ( response.content.includes(\"Mordor\")) {\n        chat.context = {\n          isChill: true,\n        }\n      } else {\n        chat.context = {\n          isChill: false,\n          tasks: [ \"watch LOTR\" ]\n        }\n      }\n      resolve();\n    }))\n    .prompt( ( context, $this ) => {\n      if ( context.isChill ) {\n        return {\n          message: \"great answer\"\n        }\n      } else {\n        return {\n          message: \"I need you to watch LOTR\"\n        }\n      }\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n## expect\nThe `expect` function is another way to access the response of a prompt and set expectations for it. When working with the Language Model (LLM), you can use this command to define specific criteria that the response should meet. If these criteria are not met, you have the option to call `reject(\"with your reason\")` to initiate a [dispute resolution](#dispute-resolution) process. On the other hand, if the response aligns with your expectations, you can use the `resolve()` method to continue the chain of interactions.\n\nThis command plays a pivotal role in ensuring that the Language Model's responses are in line with your desired outcomes. It serves as a valuable tool for quality control and enables the creation of custom interactions with the model. By setting clear expectations and handling responses accordingly, you can shape the behavior of the model to meet your specific needs.\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\ninterface Club {\n  club: string;\n  yardage: number;\n}\n\nconst main = () => {\n  const chat = new DSL<Options, Club[]>({});\n  chat\n    .prompt({\n      message: \"please generate a JSON Array of golf clubs I could hit out of the ruff. This JSON array must structured as ```javascript {\\noptions: { club: string; yardage: number; }[]\\n}``` and be provided as a JSON code block\"\n    })\n    .expect( response => new Promise<void>(( resolve, reject ) => {\n      // I have high expecations\n      if ( response.codeBlocks === undefined ) {\n        reject(\"a json code block was expected\")\n      } else if ( response.codeBlocks.length > 1 ) {\n        reject(\"too many code blocks, only 1 is required\")\n      } else {\n        const block = response.codeBlocks[0];\n        if ( block.lang !== \"json\" ) {\n          reject(\"code block must be 'json'\")\n        } else {\n          const data = JSON.parse(block.code);\n          chat.context = data.options;\n          resolve();\n        }\n      }\n    }))\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n#### Dispute Resolution\n\nWhen you call `reject(\"with your reason\")`, the DSL will initiate a `sidebar` chat to resolve the dispute between your expectations and the prior response. This `sidebar` chat retains the `conetxt`, `rules` and `functions` you originally defined in the main chat.\n\nEach response generated in the `sidebar` will be assessed against your expectations until they are met or the maximum call stack limit is reached. \n\n> Adjust the expect max call stack with `setMaxCallStack`\n\nThe response that aligns with your expectations will be marked with `Visibility.OPTIONAL` and will follow the response that triggered the dispute. The initial response that led to the dispute will be updated with a visibility of `Visibility.HIDDEN`, and the chain of commands will resume.\n\n## promptForEach\nThe `promptForEach` command allows you to use the chat `context` to generate one or more prompts, each of which is executed sequentially and synchronously. This function is particularly useful when you need to create multiple prompts dynamically based on the context data.\n\n\n```javascript\nimport { DSL } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    /** continued from above */\n    .promptForEach( ( context: Club[] ) => {\n      return context.map(({ club, yardage}, index ) => {\n        return {\n          message: `I expect the ruff to take about 40% off the distance of the club, what is the new yardarge for ${club}?`\n        }\n      })\n    })\n    .prompt({\n      message: \"I have 120y to the hole and I want to carry it 115y, which club should I use?\"\n    })\n    .prompt({\n      message: \"would this be a full swing?\"\n    })\n    .prompt({\n      message: \"I stuck it close, let's get some birds\",\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n## branchForEach\n`branchForEach` similary enables you to use the chat `context` to generate a series of commands. The series is scoped to each command between the `branchForEach` and the next `join` command. Each series is executed sequentially and synchronously.\n\n```javascript\nimport { DSL } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const LLM = new ChatGPT( {} );\n\n  const chat = new DSL<PromptOptions, Context>( {\n    llm: LLM,\n    storage: LocalStorage,\n    options: {\n      model: \"gpt-4\",\n      temperature: 0.5,\n      top_p: 0.5\n    }\n  } );\n\n  chat\n    .rule( CODE_BLOCK_RULE )\n    .prompt( {\n      message: \"generate a list of cities implementing the ```typescript interface { cities: string[] }``` as a json code block\"\n    } )\n    .expect( response => {\n      // todo - set the expectations\n      const data = JSON.parse( response.codeBlocks![0].code );\n      chat.context = data.cities;\n      resolve();\n    })\n    .branchForEach( context => {\n      return context.map( city => {\n        return {\n          message: `consider the following city, ${ city } and pick a monument, artificat or famous person in this city we'll call this the target`\n        };\n      } );\n    } )\n    .prompt( {\n      message: \"consider the RPG table top game Sprawl, create a new mission in this city that involves our target\"\n    } )\n    .response( response => {\n      // write the response to a file\n      return new Promise<void>(( resolve, reject ) => {\n        writeFileSync(`./sprawl-missions/${response.id}.txt`);\n        resolve();\n      })\n    })\n    .join()\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then( () => {\n      console.log( \"done\" );\n    } )\n    .catch( error => {\n      console.error( error );\n    } );\n};\n\nmain();\n\n```\n\n# Storage\nchats can be stored for later use; this requires a `storage` mechanism. This mechanism manages saving and retrieving chats from a data store. This package includes `LocalStorage` which will save the chats to the local file system and `NoStorage` which does not save any chats. \n\n`LocalStorage` requires an environment variable `CHATS_DIR` where the chats will be stored as `{id}.json` files \n> CHATS_DIR will not be created automatically\n```text\n# .env\nCHATS_DIR=/opt/chats\n```\n\n\n## Custom\nyou can define your own storage mechanism by implementing the `Storage` interface\n\n```javascript\nimport { ChatStorage } from \"@k-apps.io/llm-dsl\";\n\nconst MyAPI: ChatStorage = {\n  getById: ( id: string ): Promise<Chat> => {\n    return new Promise<Chat>( ( resolve, reject ) => {\n      /**\n       * TODO: retrieve an existing chat\n       */\n      reject();\n    } );\n  },\n  save: ( chat: Chat ): Promise<void> => {\n    return new Promise<void>( ( resolve, reject ) => {\n      /**\n       * TODO: save the chat\n       */\n      reject();\n    } );\n  }\n}\n\nconst main = () => {\n  const chat = new DSL({\n    storage: MyAPI,\n    // ...\n  });\n}\n\nmain();\n```\n","readmeFilename":"README.md","gitHead":"bfd18c1830cc2e5264e93905fc33e6dcdc479dfd","_id":"@k-apps.io/llm-dsl@1.2.1-beta.0","_nodeVersion":"18.13.0","_npmVersion":"9.2.0","dist":{"integrity":"sha512-HUI1Md0d9Hr5+LciGuB4b9LloDrm/0MBuwBr7C7DUK9ZyIKOodYCw6l0zVuPWYfSs66UDio9qx1N+3u5trgYqA==","shasum":"f9460a490433953135d994ee67de80fc354aa50e","tarball":"https://registry.npmjs.org/@k-apps.io/llm-dsl/-/llm-dsl-1.2.1-beta.0.tgz","fileCount":14,"unpackedSize":51510,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD4WrWckFuzgTRKXULXyHmrQSb3/0O/bfFOvG/c4EOSkAIhAMrrtuqnrrniHKBwcwgZnUJOj0ZoUF3RVZUo6Ie9WaaD"}]},"_npmUser":{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"},"directories":{},"maintainers":[{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/llm-dsl_1.2.1-beta.0_1699816411909_0.11435027068683312"},"_hasShrinkwrap":false},"1.2.1-beta.1":{"name":"@k-apps.io/llm-dsl","version":"1.2.1-beta.1","description":"a DSL for Large Language Models (LLM)","main":"dist/index.js","scripts":{"test":"jest","build":"tsc","deploy-prod":"npm run build && npm publish --access public","deploy":"npm run build && git add . && git commit -m \"Committing changes before beta deploy\" && npm version prerelease --preid=beta && npm publish --tag beta"},"repository":{"type":"git","url":"git+https://github.com/k-apps-io/llm-dsl.git"},"keywords":["LLM","chatgpt","openai"],"author":{"name":"@imnotakopp"},"license":"ISC","bugs":{"url":"https://github.com/k-apps-io/llm-dsl/issues"},"homepage":"https://github.com/k-apps-io/llm-dsl#readme","devDependencies":{"@k-apps.io/llm-dsl-chatgpt":"^1.2.1-beta.0","@types/jest":"^29.5.1","@types/lodash":"^4.14.200","@types/uuid":"^9.0.6","dotenv":"^16.3.1","jest":"^29.5.0","openai":"^4.14.2","tiktoken":"^1.0.10","ts-jest":"^29.1.0","ts-node":"^10.9.1","typescript":"^5.2.2"},"dependencies":{"ebnf":"^1.9.1","lodash":"^4.17.21","uuid":"^9.0.1"},"types":"./dist/index.d.ts","readme":"# @k-apps.io/llm-dsl\nthis is a DSL for interacting with a LLM, chaining commands together to formulate robust tasks\n\n# Installation\n\n```shell\nnpm install @k-apps.io/llm-dsl\n```\n\npair this with an LLM implementation such as\n\n[@k-apps.io/llm-dsl-chatgpt](https://www.npmjs.com/package/@k-apps.io/llm-dsl-chatgpt)\n\n# Usage\n\n### Hello World\n```javascript\nimport { LLM, DSL, LocalStorage, Rules, CODE_BLOCK_RULE } from \"@k-apps.io/llm-dsl\";\nimport { ChatGPT, Options } from \"@k-apps.io/llm-dsl-chatgpt\";\n\nrequire( \"dotenv\" ).config();\n\nconst main = () => {\n\n  const LLM = new ChatGPT({});\n\n  type Context = string[];\n\n  const chat = new DSL<Options, Context>( {\n    llm: LLM,\n    storage: LocalStorage,\n    options: {\n      model: \"gpt-4\"\n    }\n  } );\n\n  chat\n    .rule( CODE_BLOCK_RULE )\n    .prompt( {\n      message: \"Hello!\"\n    } )\n    .prompt( {\n      message: 'send me 3 progamming languages as a JSON object e.g ```json [\"lang1\", \"lang2\", \"langN\"]```',\n    } )\n    .expect( ( response ) => new Promise<void>( ( resolve, reject ) => {\n      // setting expectations for the response\n      if ( response.codeBlocks === undefined ) {\n        reject( \"expecting a json code block\" );\n      } else if ( response.codeBlocks.length > 1 ) {\n        reject( \"only 1 code block was requested\" );\n      } else {\n        const block = response.codeBlocks[ 0 ];\n        if ( block.lang !== \"json\" ) {\n          reject( `expecting a json code block, found '${ block.lang }' instead` );\n        } else {\n          const data = JSON.parse( block.code );\n          // setting the context in this command makes it available hereafter\n          chat.context = data;\n          resolve();\n        }\n      }\n    } ) )\n    .promptForEach( ( languages: Context ) => {\n      // using this context I can send more prompts\n      return languages.map( language => {\n        const message = `write me a hello world progamming in ${ language }`;\n        return { message: message };\n      } );\n    } )\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then( () => {\n      console.log( \"done\" );\n    } )\n    .catch( error => {\n      console.error( error );\n    } );\n}\n```\n\n# Rules\na `rule` represents a specific instruction for the LLM and `you` to follow. Regardless where you define these in the chain of commands they will always be included at the top of the convesation with `Visibility.REQUIRED`\n\n```javascript\nimport { DSL, Rule } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    .rule({\n      name: \"Bob Uecker\",\n      requirement: \"respond in the style of Bob Uecker, the Famous Radio Broadcaster and not so Fammous American League Baseball Player\"\n    })\n    .prompt({\n      message: \"Uecke, give me a home run call\"\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content );\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Functions\n\nIn your DSL script, you can define custom `functions` that the Language Model (LLM) may call, and the results will be returned as new `Message` objects. This feature allows you to create custom logic and interactions within your conversation with the LLM.\n\n```javascript\nimport { DSL, Rule, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    .function<{ unit?: \"Fahrenheit\" | \"Celsius\"; }>( {\n      name: \"getTheCurrentWeather\",\n      description: \"helps the AI model determine the current weather\",\n      parameters: {\n        \"type\": \"object\",\n        \"properties\": {\n          \"unit\": {\n            \"type\": \"string\",\n            \"enum\": [ \"Fahrenheit\", \"Celsius\" ]\n          }\n        }\n      },\n      func: ( { unit = \"Fahrenheit\", context } ) => {\n        return new Promise( ( resolve, reject ) => {\n          const weather = {\n            temperature: unit === \"Fahrenheit\" ? 24 : 5,\n            unit: unit,\n            percipitation: \"it's snowing\"\n          };\n          resolve( {\n            message: `here is the current weather report \\`\\`\\`json ${ JSON.stringify( weather ) }\\`\\`\\``\n          } );\n        } );\n      }\n    } )\n    .rule( {\n      name: \"Temperature\",\n      requirement: \"all temperature readings must be in Celsius\"\n    } )\n    .prompt( {\n      message: \"what's the weather like today?\"\n    } )\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content );\n    } )\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Prompting\na `prompt` is the main form of interacting with the LLM, use this command to send a message and generate a response\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL<Options, string>({});\n  chat.context = \"what is something the bank has that could be used on the football field?\";\n  chat\n    .prompt({\n      message: \"Why did the football team go to the bank?\"\n    })\n    .prompt( context => {\n      // a prompt can also access the context of the chat via a `CommandFunction`\n      return {\n        message: `here is a hint: ${context}`\n      }\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Context\n\nthe `context` is a shared space that enables the seamless exchange of information between different commands and prompts. It's a vital tool for tasks that involve gathering, adjusting, or retrieving data across multiple interactions. The `context` ensures your conversation flows cohesively and empowers you to manage and transfer artifacts effortlessly.\n\nTo manage the `context`, you can use commands like [response](#response) and [expect](#expect) or call `setContext` directly\n\nTo use the `context`, checkout [prompt](#prompting), [promptForEach](#promptforeach) and [branchForEach](#branchforeach)\n\n\n## response\n`response` provides direct accesss the response of a prompt, it gives you control to continue the chain of commands by calling `resolve` or stop by calling `reject`\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL<Options, string>({});\n  chat\n    .prompt({\n      message: \"finish the sentence 'one does not'\"\n    })\n    .response( response => new Promise<void>(( resolve, reject ) => {\n      if ( response.content.includes(\"Mordor\")) {\n        chat.context = {\n          isChill: true,\n        }\n      } else {\n        chat.context = {\n          isChill: false,\n          tasks: [ \"watch LOTR\" ]\n        }\n      }\n      resolve();\n    }))\n    .prompt( ( context, $this ) => {\n      if ( context.isChill ) {\n        return {\n          message: \"great answer\"\n        }\n      } else {\n        return {\n          message: \"I need you to watch LOTR\"\n        }\n      }\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n## expect\nThe `expect` function is another way to access the response of a prompt and set expectations for it. When working with the Language Model (LLM), you can use this command to define specific criteria that the response should meet. If these criteria are not met, you have the option to call `reject(\"with your reason\")` to initiate a [dispute resolution](#dispute-resolution) process. On the other hand, if the response aligns with your expectations, you can use the `resolve()` method to continue the chain of interactions.\n\nThis command plays a pivotal role in ensuring that the Language Model's responses are in line with your desired outcomes. It serves as a valuable tool for quality control and enables the creation of custom interactions with the model. By setting clear expectations and handling responses accordingly, you can shape the behavior of the model to meet your specific needs.\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\ninterface Club {\n  club: string;\n  yardage: number;\n}\n\nconst main = () => {\n  const chat = new DSL<Options, Club[]>({});\n  chat\n    .prompt({\n      message: \"please generate a JSON Array of golf clubs I could hit out of the ruff. This JSON array must structured as ```javascript {\\noptions: { club: string; yardage: number; }[]\\n}``` and be provided as a JSON code block\"\n    })\n    .expect( response => new Promise<void>(( resolve, reject ) => {\n      // I have high expecations\n      if ( response.codeBlocks === undefined ) {\n        reject(\"a json code block was expected\")\n      } else if ( response.codeBlocks.length > 1 ) {\n        reject(\"too many code blocks, only 1 is required\")\n      } else {\n        const block = response.codeBlocks[0];\n        if ( block.lang !== \"json\" ) {\n          reject(\"code block must be 'json'\")\n        } else {\n          const data = JSON.parse(block.code);\n          chat.context = data.options;\n          resolve();\n        }\n      }\n    }))\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n#### Dispute Resolution\n\nWhen you call `reject(\"with your reason\")`, the DSL will initiate a `sidebar` chat to resolve the dispute between your expectations and the prior response. This `sidebar` chat retains the `conetxt`, `rules` and `functions` you originally defined in the main chat.\n\nEach response generated in the `sidebar` will be assessed against your expectations until they are met or the maximum call stack limit is reached. \n\n> Adjust the expect max call stack with `setMaxCallStack`\n\nThe response that aligns with your expectations will be marked with `Visibility.OPTIONAL` and will follow the response that triggered the dispute. The initial response that led to the dispute will be updated with a visibility of `Visibility.HIDDEN`, and the chain of commands will resume.\n\n## promptForEach\nThe `promptForEach` command allows you to use the chat `context` to generate one or more prompts, each of which is executed sequentially and synchronously. This function is particularly useful when you need to create multiple prompts dynamically based on the context data.\n\n\n```javascript\nimport { DSL } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    /** continued from above */\n    .promptForEach( ( context: Club[] ) => {\n      return context.map(({ club, yardage}, index ) => {\n        return {\n          message: `I expect the ruff to take about 40% off the distance of the club, what is the new yardarge for ${club}?`\n        }\n      })\n    })\n    .prompt({\n      message: \"I have 120y to the hole and I want to carry it 115y, which club should I use?\"\n    })\n    .prompt({\n      message: \"would this be a full swing?\"\n    })\n    .prompt({\n      message: \"I stuck it close, let's get some birds\",\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n## branchForEach\n`branchForEach` similary enables you to use the chat `context` to generate a series of commands. The series is scoped to each command between the `branchForEach` and the next `join` command. Each series is executed sequentially and synchronously.\n\n```javascript\nimport { DSL } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const LLM = new ChatGPT( {} );\n\n  const chat = new DSL<PromptOptions, Context>( {\n    llm: LLM,\n    storage: LocalStorage,\n    options: {\n      model: \"gpt-4\",\n      temperature: 0.5,\n      top_p: 0.5\n    }\n  } );\n\n  chat\n    .rule( CODE_BLOCK_RULE )\n    .prompt( {\n      message: \"generate a list of cities implementing the ```typescript interface { cities: string[] }``` as a json code block\"\n    } )\n    .expect( response => {\n      // todo - set the expectations\n      const data = JSON.parse( response.codeBlocks![0].code );\n      chat.context = data.cities;\n      resolve();\n    })\n    .branchForEach( context => {\n      return context.map( city => {\n        return {\n          message: `consider the following city, ${ city } and pick a monument, artificat or famous person in this city we'll call this the target`\n        };\n      } );\n    } )\n    .prompt( {\n      message: \"consider the RPG table top game Sprawl, create a new mission in this city that involves our target\"\n    } )\n    .response( response => {\n      // write the response to a file\n      return new Promise<void>(( resolve, reject ) => {\n        writeFileSync(`./sprawl-missions/${response.id}.txt`);\n        resolve();\n      })\n    })\n    .join()\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then( () => {\n      console.log( \"done\" );\n    } )\n    .catch( error => {\n      console.error( error );\n    } );\n};\n\nmain();\n\n```\n\n# Storage\nchats can be stored for later use; this requires a `storage` mechanism. This mechanism manages saving and retrieving chats from a data store. This package includes `LocalStorage` which will save the chats to the local file system and `NoStorage` which does not save any chats. \n\n`LocalStorage` requires an environment variable `CHATS_DIR` where the chats will be stored as `{id}.json` files \n> CHATS_DIR will not be created automatically\n```text\n# .env\nCHATS_DIR=/opt/chats\n```\n\n\n## Custom\nyou can define your own storage mechanism by implementing the `Storage` interface\n\n```javascript\nimport { ChatStorage } from \"@k-apps.io/llm-dsl\";\n\nconst MyAPI: ChatStorage = {\n  getById: ( id: string ): Promise<Chat> => {\n    return new Promise<Chat>( ( resolve, reject ) => {\n      /**\n       * TODO: retrieve an existing chat\n       */\n      reject();\n    } );\n  },\n  save: ( chat: Chat ): Promise<void> => {\n    return new Promise<void>( ( resolve, reject ) => {\n      /**\n       * TODO: save the chat\n       */\n      reject();\n    } );\n  }\n}\n\nconst main = () => {\n  const chat = new DSL({\n    storage: MyAPI,\n    // ...\n  });\n}\n\nmain();\n```\n","readmeFilename":"README.md","gitHead":"175e791938ea2c73ab6020610882cf4e5bde7cb6","_id":"@k-apps.io/llm-dsl@1.2.1-beta.1","_nodeVersion":"18.13.0","_npmVersion":"9.2.0","dist":{"integrity":"sha512-wytg+u0Et0G2ftFK+8PxmdofKN8xSpeEYbkPg7M27ethgk4R9x1StG5Tf7sMO6/VBjMfPquGRmE9q2oxVOAjUg==","shasum":"74b9d798ce20ed51e17512caeeb9ecf7d3f70942","tarball":"https://registry.npmjs.org/@k-apps.io/llm-dsl/-/llm-dsl-1.2.1-beta.1.tgz","fileCount":14,"unpackedSize":53438,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICp6OeQQ39zOVqHzv6d+tnR9GBQglznNmdZFW9qGZRw/AiBCZvMDk0AbcxLDw36NGu0MKNp6p2QCHijX2kw8PiEr6Q=="}]},"_npmUser":{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"},"directories":{},"maintainers":[{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/llm-dsl_1.2.1-beta.1_1699850601583_0.7661550690951386"},"_hasShrinkwrap":false},"1.2.1-beta.2":{"name":"@k-apps.io/llm-dsl","version":"1.2.1-beta.2","description":"a DSL for Large Language Models (LLM)","main":"dist/index.js","scripts":{"test":"jest","build":"tsc","deploy-prod":"npm run build && npm publish --access public","deploy":"npm run build && git add . && git commit -m \"Committing changes before beta deploy\" && npm version prerelease --preid=beta && npm publish --tag beta"},"repository":{"type":"git","url":"git+https://github.com/k-apps-io/llm-dsl.git"},"keywords":["LLM","chatgpt","openai"],"author":{"name":"@imnotakopp"},"license":"ISC","bugs":{"url":"https://github.com/k-apps-io/llm-dsl/issues"},"homepage":"https://github.com/k-apps-io/llm-dsl#readme","devDependencies":{"@k-apps.io/llm-dsl-chatgpt":"^1.2.1-beta.0","@types/jest":"^29.5.1","@types/lodash":"^4.14.200","@types/uuid":"^9.0.6","dotenv":"^16.3.1","jest":"^29.5.0","openai":"^4.14.2","tiktoken":"^1.0.10","ts-jest":"^29.1.0","ts-node":"^10.9.1","typescript":"^5.2.2"},"dependencies":{"ebnf":"^1.9.1","json5":"^2.2.3","lodash":"^4.17.21","uuid":"^9.0.1"},"types":"./dist/index.d.ts","readme":"# @k-apps.io/llm-dsl\nthis is a DSL for interacting with a LLM, chaining commands together to formulate robust tasks\n\n# Installation\n\n```shell\nnpm install @k-apps.io/llm-dsl\n```\n\npair this with an LLM implementation such as\n\n[@k-apps.io/llm-dsl-chatgpt](https://www.npmjs.com/package/@k-apps.io/llm-dsl-chatgpt)\n\n# Usage\n\n### Hello World\n```javascript\nimport { LLM, DSL, LocalStorage, Rules, CODE_BLOCK_RULE } from \"@k-apps.io/llm-dsl\";\nimport { ChatGPT, Options } from \"@k-apps.io/llm-dsl-chatgpt\";\n\nrequire( \"dotenv\" ).config();\n\nconst main = () => {\n\n  const LLM = new ChatGPT({});\n\n  type Context = string[];\n\n  const chat = new DSL<Options, Context>( {\n    llm: LLM,\n    storage: LocalStorage,\n    options: {\n      model: \"gpt-4\"\n    }\n  } );\n\n  chat\n    .rule( CODE_BLOCK_RULE )\n    .prompt( {\n      message: \"Hello!\"\n    } )\n    .prompt( {\n      message: 'send me 3 progamming languages as a JSON object e.g ```json [\"lang1\", \"lang2\", \"langN\"]```',\n    } )\n    .expect( ( response ) => new Promise<void>( ( resolve, reject ) => {\n      // setting expectations for the response\n      if ( response.codeBlocks === undefined ) {\n        reject( \"expecting a json code block\" );\n      } else if ( response.codeBlocks.length > 1 ) {\n        reject( \"only 1 code block was requested\" );\n      } else {\n        const block = response.codeBlocks[ 0 ];\n        if ( block.lang !== \"json\" ) {\n          reject( `expecting a json code block, found '${ block.lang }' instead` );\n        } else {\n          const data = JSON.parse( block.code );\n          // setting the context in this command makes it available hereafter\n          chat.context = data;\n          resolve();\n        }\n      }\n    } ) )\n    .promptForEach( ( languages: Context ) => {\n      // using this context I can send more prompts\n      return languages.map( language => {\n        const message = `write me a hello world progamming in ${ language }`;\n        return { message: message };\n      } );\n    } )\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then( () => {\n      console.log( \"done\" );\n    } )\n    .catch( error => {\n      console.error( error );\n    } );\n}\n```\n\n# Rules\na `rule` represents a specific instruction for the LLM and `you` to follow. Regardless where you define these in the chain of commands they will always be included at the top of the convesation with `Visibility.REQUIRED`\n\n```javascript\nimport { DSL, Rule } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    .rule({\n      name: \"Bob Uecker\",\n      requirement: \"respond in the style of Bob Uecker, the Famous Radio Broadcaster and not so Fammous American League Baseball Player\"\n    })\n    .prompt({\n      message: \"Uecke, give me a home run call\"\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content );\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Functions\n\nIn your DSL script, you can define custom `functions` that the Language Model (LLM) may call, and the results will be returned as new `Message` objects. This feature allows you to create custom logic and interactions within your conversation with the LLM.\n\n```javascript\nimport { DSL, Rule, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    .function<{ unit?: \"Fahrenheit\" | \"Celsius\"; }>( {\n      name: \"getTheCurrentWeather\",\n      description: \"helps the AI model determine the current weather\",\n      parameters: {\n        \"type\": \"object\",\n        \"properties\": {\n          \"unit\": {\n            \"type\": \"string\",\n            \"enum\": [ \"Fahrenheit\", \"Celsius\" ]\n          }\n        }\n      },\n      func: ( { unit = \"Fahrenheit\", context } ) => {\n        return new Promise( ( resolve, reject ) => {\n          const weather = {\n            temperature: unit === \"Fahrenheit\" ? 24 : 5,\n            unit: unit,\n            percipitation: \"it's snowing\"\n          };\n          resolve( {\n            message: `here is the current weather report \\`\\`\\`json ${ JSON.stringify( weather ) }\\`\\`\\``\n          } );\n        } );\n      }\n    } )\n    .rule( {\n      name: \"Temperature\",\n      requirement: \"all temperature readings must be in Celsius\"\n    } )\n    .prompt( {\n      message: \"what's the weather like today?\"\n    } )\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content );\n    } )\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Prompting\na `prompt` is the main form of interacting with the LLM, use this command to send a message and generate a response\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL<Options, string>({});\n  chat.context = \"what is something the bank has that could be used on the football field?\";\n  chat\n    .prompt({\n      message: \"Why did the football team go to the bank?\"\n    })\n    .prompt( context => {\n      // a prompt can also access the context of the chat via a `CommandFunction`\n      return {\n        message: `here is a hint: ${context}`\n      }\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Context\n\nthe `context` is a shared space that enables the seamless exchange of information between different commands and prompts. It's a vital tool for tasks that involve gathering, adjusting, or retrieving data across multiple interactions. The `context` ensures your conversation flows cohesively and empowers you to manage and transfer artifacts effortlessly.\n\nTo manage the `context`, you can use commands like [response](#response) and [expect](#expect) or call `setContext` directly\n\nTo use the `context`, checkout [prompt](#prompting), [promptForEach](#promptforeach) and [branchForEach](#branchforeach)\n\n\n## response\n`response` provides direct accesss the response of a prompt, it gives you control to continue the chain of commands by calling `resolve` or stop by calling `reject`\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL<Options, string>({});\n  chat\n    .prompt({\n      message: \"finish the sentence 'one does not'\"\n    })\n    .response( response => new Promise<void>(( resolve, reject ) => {\n      if ( response.content.includes(\"Mordor\")) {\n        chat.context = {\n          isChill: true,\n        }\n      } else {\n        chat.context = {\n          isChill: false,\n          tasks: [ \"watch LOTR\" ]\n        }\n      }\n      resolve();\n    }))\n    .prompt( ( context, $this ) => {\n      if ( context.isChill ) {\n        return {\n          message: \"great answer\"\n        }\n      } else {\n        return {\n          message: \"I need you to watch LOTR\"\n        }\n      }\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n## expect\nThe `expect` function is another way to access the response of a prompt and set expectations for it. When working with the Language Model (LLM), you can use this command to define specific criteria that the response should meet. If these criteria are not met, you have the option to call `reject(\"with your reason\")` to initiate a [dispute resolution](#dispute-resolution) process. On the other hand, if the response aligns with your expectations, you can use the `resolve()` method to continue the chain of interactions.\n\nThis command plays a pivotal role in ensuring that the Language Model's responses are in line with your desired outcomes. It serves as a valuable tool for quality control and enables the creation of custom interactions with the model. By setting clear expectations and handling responses accordingly, you can shape the behavior of the model to meet your specific needs.\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\ninterface Club {\n  club: string;\n  yardage: number;\n}\n\nconst main = () => {\n  const chat = new DSL<Options, Club[]>({});\n  chat\n    .prompt({\n      message: \"please generate a JSON Array of golf clubs I could hit out of the ruff. This JSON array must structured as ```javascript {\\noptions: { club: string; yardage: number; }[]\\n}``` and be provided as a JSON code block\"\n    })\n    .expect( response => new Promise<void>(( resolve, reject ) => {\n      // I have high expecations\n      if ( response.codeBlocks === undefined ) {\n        reject(\"a json code block was expected\")\n      } else if ( response.codeBlocks.length > 1 ) {\n        reject(\"too many code blocks, only 1 is required\")\n      } else {\n        const block = response.codeBlocks[0];\n        if ( block.lang !== \"json\" ) {\n          reject(\"code block must be 'json'\")\n        } else {\n          const data = JSON.parse(block.code);\n          chat.context = data.options;\n          resolve();\n        }\n      }\n    }))\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n#### Dispute Resolution\n\nWhen you call `reject(\"with your reason\")`, the DSL will initiate a `sidebar` chat to resolve the dispute between your expectations and the prior response. This `sidebar` chat retains the `conetxt`, `rules` and `functions` you originally defined in the main chat.\n\nEach response generated in the `sidebar` will be assessed against your expectations until they are met or the maximum call stack limit is reached. \n\n> Adjust the expect max call stack with `setMaxCallStack`\n\nThe response that aligns with your expectations will be marked with `Visibility.OPTIONAL` and will follow the response that triggered the dispute. The initial response that led to the dispute will be updated with a visibility of `Visibility.HIDDEN`, and the chain of commands will resume.\n\n## promptForEach\nThe `promptForEach` command allows you to use the chat `context` to generate one or more prompts, each of which is executed sequentially and synchronously. This function is particularly useful when you need to create multiple prompts dynamically based on the context data.\n\n\n```javascript\nimport { DSL } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    /** continued from above */\n    .promptForEach( ( context: Club[] ) => {\n      return context.map(({ club, yardage}, index ) => {\n        return {\n          message: `I expect the ruff to take about 40% off the distance of the club, what is the new yardarge for ${club}?`\n        }\n      })\n    })\n    .prompt({\n      message: \"I have 120y to the hole and I want to carry it 115y, which club should I use?\"\n    })\n    .prompt({\n      message: \"would this be a full swing?\"\n    })\n    .prompt({\n      message: \"I stuck it close, let's get some birds\",\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n## branchForEach\n`branchForEach` similary enables you to use the chat `context` to generate a series of commands. The series is scoped to each command between the `branchForEach` and the next `join` command. Each series is executed sequentially and synchronously.\n\n```javascript\nimport { DSL } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const LLM = new ChatGPT( {} );\n\n  const chat = new DSL<PromptOptions, Context>( {\n    llm: LLM,\n    storage: LocalStorage,\n    options: {\n      model: \"gpt-4\",\n      temperature: 0.5,\n      top_p: 0.5\n    }\n  } );\n\n  chat\n    .rule( CODE_BLOCK_RULE )\n    .prompt( {\n      message: \"generate a list of cities implementing the ```typescript interface { cities: string[] }``` as a json code block\"\n    } )\n    .expect( response => {\n      // todo - set the expectations\n      const data = JSON.parse( response.codeBlocks![0].code );\n      chat.context = data.cities;\n      resolve();\n    })\n    .branchForEach( context => {\n      return context.map( city => {\n        return {\n          message: `consider the following city, ${ city } and pick a monument, artificat or famous person in this city we'll call this the target`\n        };\n      } );\n    } )\n    .prompt( {\n      message: \"consider the RPG table top game Sprawl, create a new mission in this city that involves our target\"\n    } )\n    .response( response => {\n      // write the response to a file\n      return new Promise<void>(( resolve, reject ) => {\n        writeFileSync(`./sprawl-missions/${response.id}.txt`);\n        resolve();\n      })\n    })\n    .join()\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then( () => {\n      console.log( \"done\" );\n    } )\n    .catch( error => {\n      console.error( error );\n    } );\n};\n\nmain();\n\n```\n\n# Storage\nchats can be stored for later use; this requires a `storage` mechanism. This mechanism manages saving and retrieving chats from a data store. This package includes `LocalStorage` which will save the chats to the local file system and `NoStorage` which does not save any chats. \n\n`LocalStorage` requires an environment variable `CHATS_DIR` where the chats will be stored as `{id}.json` files \n> CHATS_DIR will not be created automatically\n```text\n# .env\nCHATS_DIR=/opt/chats\n```\n\n\n## Custom\nyou can define your own storage mechanism by implementing the `Storage` interface\n\n```javascript\nimport { ChatStorage } from \"@k-apps.io/llm-dsl\";\n\nconst MyAPI: ChatStorage = {\n  getById: ( id: string ): Promise<Chat> => {\n    return new Promise<Chat>( ( resolve, reject ) => {\n      /**\n       * TODO: retrieve an existing chat\n       */\n      reject();\n    } );\n  },\n  save: ( chat: Chat ): Promise<void> => {\n    return new Promise<void>( ( resolve, reject ) => {\n      /**\n       * TODO: save the chat\n       */\n      reject();\n    } );\n  }\n}\n\nconst main = () => {\n  const chat = new DSL({\n    storage: MyAPI,\n    // ...\n  });\n}\n\nmain();\n```\n","readmeFilename":"README.md","gitHead":"e6756b362767abdb6ee2f6024782165aa13d222a","_id":"@k-apps.io/llm-dsl@1.2.1-beta.2","_nodeVersion":"18.13.0","_npmVersion":"9.2.0","dist":{"integrity":"sha512-/Krmpp446vkH1+3Zkc3yQAkARmSMwQdKxZxUV45ZQyyItK85lbQRe2+0D21srAv0borxxTZsTd9IjzCf6+HcUw==","shasum":"c568b57328977f3ff461ba5cfff9514f5690da1e","tarball":"https://registry.npmjs.org/@k-apps.io/llm-dsl/-/llm-dsl-1.2.1-beta.2.tgz","fileCount":14,"unpackedSize":57145,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHxf+gzZQWGyPde955+q88lcMxdySpdxzEtfvDXa04OvAiEA5EymCFgaS/vZwH+S7icOpW1POPxVarD6TAKFbsqNIlI="}]},"_npmUser":{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"},"directories":{},"maintainers":[{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/llm-dsl_1.2.1-beta.2_1700450744800_0.9718117056738849"},"_hasShrinkwrap":false},"1.2.1-beta.3":{"name":"@k-apps.io/llm-dsl","version":"1.2.1-beta.3","description":"a DSL for Large Language Models (LLM)","main":"dist/index.js","scripts":{"test":"jest","build":"tsc","deploy-prod":"npm run build && npm publish --access public","deploy":"npm run build && git add . && git commit -m \"Committing changes before beta deploy\" && npm version prerelease --preid=beta && npm publish --tag beta"},"repository":{"type":"git","url":"git+https://github.com/k-apps-io/llm-dsl.git"},"keywords":["LLM","chatgpt","openai"],"author":{"name":"@imnotakopp"},"license":"ISC","bugs":{"url":"https://github.com/k-apps-io/llm-dsl/issues"},"homepage":"https://github.com/k-apps-io/llm-dsl#readme","devDependencies":{"@k-apps.io/llm-dsl-chatgpt":"^1.2.1-beta.0","@types/jest":"^29.5.1","@types/lodash":"^4.14.200","@types/uuid":"^9.0.6","dotenv":"^16.3.1","jest":"^29.5.0","openai":"^4.14.2","tiktoken":"^1.0.10","ts-jest":"^29.1.0","ts-node":"^10.9.1","typescript":"^5.2.2"},"dependencies":{"ebnf":"^1.9.1","json5":"^2.2.3","lodash":"^4.17.21","uuid":"^9.0.1"},"types":"./dist/index.d.ts","readme":"# @k-apps.io/llm-dsl\nthis is a DSL for interacting with a LLM, chaining commands together to formulate robust tasks\n\n# Installation\n\n```shell\nnpm install @k-apps.io/llm-dsl\n```\n\npair this with an LLM implementation such as\n\n[@k-apps.io/llm-dsl-chatgpt](https://www.npmjs.com/package/@k-apps.io/llm-dsl-chatgpt)\n\n# Usage\n\n### Hello World\n```javascript\nimport { LLM, DSL, LocalStorage, Rules, CODE_BLOCK_RULE } from \"@k-apps.io/llm-dsl\";\nimport { ChatGPT, Options } from \"@k-apps.io/llm-dsl-chatgpt\";\n\nrequire( \"dotenv\" ).config();\n\nconst main = () => {\n\n  const LLM = new ChatGPT({});\n\n  type Context = string[];\n\n  const chat = new DSL<Options, Context>( {\n    llm: LLM,\n    storage: LocalStorage,\n    options: {\n      model: \"gpt-4\"\n    }\n  } );\n\n  chat\n    .rule( CODE_BLOCK_RULE )\n    .prompt( {\n      message: \"Hello!\"\n    } )\n    .prompt( {\n      message: 'send me 3 progamming languages as a JSON object e.g ```json [\"lang1\", \"lang2\", \"langN\"]```',\n    } )\n    .expect( ( response ) => new Promise<void>( ( resolve, reject ) => {\n      // setting expectations for the response\n      if ( response.codeBlocks === undefined ) {\n        reject( \"expecting a json code block\" );\n      } else if ( response.codeBlocks.length > 1 ) {\n        reject( \"only 1 code block was requested\" );\n      } else {\n        const block = response.codeBlocks[ 0 ];\n        if ( block.lang !== \"json\" ) {\n          reject( `expecting a json code block, found '${ block.lang }' instead` );\n        } else {\n          const data = JSON.parse( block.code );\n          // setting the context in this command makes it available hereafter\n          chat.context = data;\n          resolve();\n        }\n      }\n    } ) )\n    .promptForEach( ( languages: Context ) => {\n      // using this context I can send more prompts\n      return languages.map( language => {\n        const message = `write me a hello world progamming in ${ language }`;\n        return { message: message };\n      } );\n    } )\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then( () => {\n      console.log( \"done\" );\n    } )\n    .catch( error => {\n      console.error( error );\n    } );\n}\n```\n\n# Rules\na `rule` represents a specific instruction for the LLM and `you` to follow. Regardless where you define these in the chain of commands they will always be included at the top of the convesation with `Visibility.REQUIRED`\n\n```javascript\nimport { DSL, Rule } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    .rule({\n      name: \"Bob Uecker\",\n      requirement: \"respond in the style of Bob Uecker, the Famous Radio Broadcaster and not so Fammous American League Baseball Player\"\n    })\n    .prompt({\n      message: \"Uecke, give me a home run call\"\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content );\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Functions\n\nIn your DSL script, you can define custom `functions` that the Language Model (LLM) may call, and the results will be returned as new `Message` objects. This feature allows you to create custom logic and interactions within your conversation with the LLM.\n\n```javascript\nimport { DSL, Rule, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    .function<{ unit?: \"Fahrenheit\" | \"Celsius\"; }>( {\n      name: \"getTheCurrentWeather\",\n      description: \"helps the AI model determine the current weather\",\n      parameters: {\n        \"type\": \"object\",\n        \"properties\": {\n          \"unit\": {\n            \"type\": \"string\",\n            \"enum\": [ \"Fahrenheit\", \"Celsius\" ]\n          }\n        }\n      },\n      func: ( { unit = \"Fahrenheit\", context } ) => {\n        return new Promise( ( resolve, reject ) => {\n          const weather = {\n            temperature: unit === \"Fahrenheit\" ? 24 : 5,\n            unit: unit,\n            percipitation: \"it's snowing\"\n          };\n          resolve( {\n            message: `here is the current weather report \\`\\`\\`json ${ JSON.stringify( weather ) }\\`\\`\\``\n          } );\n        } );\n      }\n    } )\n    .rule( {\n      name: \"Temperature\",\n      requirement: \"all temperature readings must be in Celsius\"\n    } )\n    .prompt( {\n      message: \"what's the weather like today?\"\n    } )\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content );\n    } )\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Prompting\na `prompt` is the main form of interacting with the LLM, use this command to send a message and generate a response\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL<Options, string>({});\n  chat.context = \"what is something the bank has that could be used on the football field?\";\n  chat\n    .prompt({\n      message: \"Why did the football team go to the bank?\"\n    })\n    .prompt( context => {\n      // a prompt can also access the context of the chat via a `CommandFunction`\n      return {\n        message: `here is a hint: ${context}`\n      }\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Context\n\nthe `context` is a shared space that enables the seamless exchange of information between different commands and prompts. It's a vital tool for tasks that involve gathering, adjusting, or retrieving data across multiple interactions. The `context` ensures your conversation flows cohesively and empowers you to manage and transfer artifacts effortlessly.\n\nTo manage the `context`, you can use commands like [response](#response) and [expect](#expect) or call `setContext` directly\n\nTo use the `context`, checkout [prompt](#prompting), [promptForEach](#promptforeach) and [branchForEach](#branchforeach)\n\n\n## response\n`response` provides direct accesss the response of a prompt, it gives you control to continue the chain of commands by calling `resolve` or stop by calling `reject`\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL<Options, string>({});\n  chat\n    .prompt({\n      message: \"finish the sentence 'one does not'\"\n    })\n    .response( response => new Promise<void>(( resolve, reject ) => {\n      if ( response.content.includes(\"Mordor\")) {\n        chat.context = {\n          isChill: true,\n        }\n      } else {\n        chat.context = {\n          isChill: false,\n          tasks: [ \"watch LOTR\" ]\n        }\n      }\n      resolve();\n    }))\n    .prompt( ( context, $this ) => {\n      if ( context.isChill ) {\n        return {\n          message: \"great answer\"\n        }\n      } else {\n        return {\n          message: \"I need you to watch LOTR\"\n        }\n      }\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n## expect\nThe `expect` function is another way to access the response of a prompt and set expectations for it. When working with the Language Model (LLM), you can use this command to define specific criteria that the response should meet. If these criteria are not met, you have the option to call `reject(\"with your reason\")` to initiate a [dispute resolution](#dispute-resolution) process. On the other hand, if the response aligns with your expectations, you can use the `resolve()` method to continue the chain of interactions.\n\nThis command plays a pivotal role in ensuring that the Language Model's responses are in line with your desired outcomes. It serves as a valuable tool for quality control and enables the creation of custom interactions with the model. By setting clear expectations and handling responses accordingly, you can shape the behavior of the model to meet your specific needs.\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\ninterface Club {\n  club: string;\n  yardage: number;\n}\n\nconst main = () => {\n  const chat = new DSL<Options, Club[]>({});\n  chat\n    .prompt({\n      message: \"please generate a JSON Array of golf clubs I could hit out of the ruff. This JSON array must structured as ```javascript {\\noptions: { club: string; yardage: number; }[]\\n}``` and be provided as a JSON code block\"\n    })\n    .expect( response => new Promise<void>(( resolve, reject ) => {\n      // I have high expecations\n      if ( response.codeBlocks === undefined ) {\n        reject(\"a json code block was expected\")\n      } else if ( response.codeBlocks.length > 1 ) {\n        reject(\"too many code blocks, only 1 is required\")\n      } else {\n        const block = response.codeBlocks[0];\n        if ( block.lang !== \"json\" ) {\n          reject(\"code block must be 'json'\")\n        } else {\n          const data = JSON.parse(block.code);\n          chat.context = data.options;\n          resolve();\n        }\n      }\n    }))\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n#### Dispute Resolution\n\nWhen you call `reject(\"with your reason\")`, the DSL will initiate a `sidebar` chat to resolve the dispute between your expectations and the prior response. This `sidebar` chat retains the `conetxt`, `rules` and `functions` you originally defined in the main chat.\n\nEach response generated in the `sidebar` will be assessed against your expectations until they are met or the maximum call stack limit is reached. \n\n> Adjust the expect max call stack with `setMaxCallStack`\n\nThe response that aligns with your expectations will be marked with `Visibility.OPTIONAL` and will follow the response that triggered the dispute. The initial response that led to the dispute will be updated with a visibility of `Visibility.HIDDEN`, and the chain of commands will resume.\n\n## promptForEach\nThe `promptForEach` command allows you to use the chat `context` to generate one or more prompts, each of which is executed sequentially and synchronously. This function is particularly useful when you need to create multiple prompts dynamically based on the context data.\n\n\n```javascript\nimport { DSL } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    /** continued from above */\n    .promptForEach( ( context: Club[] ) => {\n      return context.map(({ club, yardage}, index ) => {\n        return {\n          message: `I expect the ruff to take about 40% off the distance of the club, what is the new yardarge for ${club}?`\n        }\n      })\n    })\n    .prompt({\n      message: \"I have 120y to the hole and I want to carry it 115y, which club should I use?\"\n    })\n    .prompt({\n      message: \"would this be a full swing?\"\n    })\n    .prompt({\n      message: \"I stuck it close, let's get some birds\",\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n## branchForEach\n`branchForEach` similary enables you to use the chat `context` to generate a series of commands. The series is scoped to each command between the `branchForEach` and the next `join` command. Each series is executed sequentially and synchronously.\n\n```javascript\nimport { DSL } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const LLM = new ChatGPT( {} );\n\n  const chat = new DSL<PromptOptions, Context>( {\n    llm: LLM,\n    storage: LocalStorage,\n    options: {\n      model: \"gpt-4\",\n      temperature: 0.5,\n      top_p: 0.5\n    }\n  } );\n\n  chat\n    .rule( CODE_BLOCK_RULE )\n    .prompt( {\n      message: \"generate a list of cities implementing the ```typescript interface { cities: string[] }``` as a json code block\"\n    } )\n    .expect( response => {\n      // todo - set the expectations\n      const data = JSON.parse( response.codeBlocks![0].code );\n      chat.context = data.cities;\n      resolve();\n    })\n    .branchForEach( context => {\n      return context.map( city => {\n        return {\n          message: `consider the following city, ${ city } and pick a monument, artificat or famous person in this city we'll call this the target`\n        };\n      } );\n    } )\n    .prompt( {\n      message: \"consider the RPG table top game Sprawl, create a new mission in this city that involves our target\"\n    } )\n    .response( response => {\n      // write the response to a file\n      return new Promise<void>(( resolve, reject ) => {\n        writeFileSync(`./sprawl-missions/${response.id}.txt`);\n        resolve();\n      })\n    })\n    .join()\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then( () => {\n      console.log( \"done\" );\n    } )\n    .catch( error => {\n      console.error( error );\n    } );\n};\n\nmain();\n\n```\n\n# Storage\nchats can be stored for later use; this requires a `storage` mechanism. This mechanism manages saving and retrieving chats from a data store. This package includes `LocalStorage` which will save the chats to the local file system and `NoStorage` which does not save any chats. \n\n`LocalStorage` requires an environment variable `CHATS_DIR` where the chats will be stored as `{id}.json` files \n> CHATS_DIR will not be created automatically\n```text\n# .env\nCHATS_DIR=/opt/chats\n```\n\n\n## Custom\nyou can define your own storage mechanism by implementing the `Storage` interface\n\n```javascript\nimport { ChatStorage } from \"@k-apps.io/llm-dsl\";\n\nconst MyAPI: ChatStorage = {\n  getById: ( id: string ): Promise<Chat> => {\n    return new Promise<Chat>( ( resolve, reject ) => {\n      /**\n       * TODO: retrieve an existing chat\n       */\n      reject();\n    } );\n  },\n  save: ( chat: Chat ): Promise<void> => {\n    return new Promise<void>( ( resolve, reject ) => {\n      /**\n       * TODO: save the chat\n       */\n      reject();\n    } );\n  }\n}\n\nconst main = () => {\n  const chat = new DSL({\n    storage: MyAPI,\n    // ...\n  });\n}\n\nmain();\n```\n","readmeFilename":"README.md","gitHead":"72615576c4f8bb129fe014391b9d4e912345fc22","_id":"@k-apps.io/llm-dsl@1.2.1-beta.3","_nodeVersion":"18.13.0","_npmVersion":"9.2.0","dist":{"integrity":"sha512-Hgeeb1T7KDvavaY53Dxcnm4i3HpwqFIei5q0H1yO5h5L8CN93vB3tnk2zHLCKmek+HpdTAd99N6WQl321yDpZA==","shasum":"d533541ed3af51f4797533b033b7b096d3ec4d65","tarball":"https://registry.npmjs.org/@k-apps.io/llm-dsl/-/llm-dsl-1.2.1-beta.3.tgz","fileCount":14,"unpackedSize":57344,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDmB3nkMCdSYh4PmYAfG22vmeBRm1Axs81rdvbsGvj2vwIhALOYDnk0PNlghCpB2xh2FBFnnmqrNxPNEsTw/YzSnZ4K"}]},"_npmUser":{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"},"directories":{},"maintainers":[{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/llm-dsl_1.2.1-beta.3_1700587394447_0.1438485973644832"},"_hasShrinkwrap":false},"1.2.1-beta.4":{"name":"@k-apps.io/llm-dsl","version":"1.2.1-beta.4","description":"a DSL for Large Language Models (LLM)","main":"dist/index.js","scripts":{"test":"jest","build":"tsc","deploy-prod":"npm run build && npm publish --access public","deploy":"npm run build && git add . && git commit -m \"Committing changes before beta deploy\" && npm version prerelease --preid=beta && npm publish --tag beta"},"repository":{"type":"git","url":"git+https://github.com/k-apps-io/llm-dsl.git"},"keywords":["LLM","chatgpt","openai"],"author":{"name":"@imnotakopp"},"license":"ISC","bugs":{"url":"https://github.com/k-apps-io/llm-dsl/issues"},"homepage":"https://github.com/k-apps-io/llm-dsl#readme","devDependencies":{"@k-apps.io/llm-dsl-chatgpt":"^1.2.1-beta.0","@types/jest":"^29.5.1","@types/lodash":"^4.14.200","@types/uuid":"^9.0.6","dotenv":"^16.3.1","jest":"^29.5.0","openai":"^4.14.2","tiktoken":"^1.0.10","ts-jest":"^29.1.0","ts-node":"^10.9.1","typescript":"^5.2.2"},"dependencies":{"ebnf":"^1.9.1","json5":"^2.2.3","lodash":"^4.17.21","uuid":"^9.0.1"},"types":"./dist/index.d.ts","readme":"# @k-apps.io/llm-dsl\nthis is a DSL for interacting with a LLM, chaining commands together to formulate robust tasks\n\n# Installation\n\n```shell\nnpm install @k-apps.io/llm-dsl\n```\n\npair this with an LLM implementation such as\n\n[@k-apps.io/llm-dsl-chatgpt](https://www.npmjs.com/package/@k-apps.io/llm-dsl-chatgpt)\n\n# Usage\n\n### Hello World\n```javascript\nimport { LLM, DSL, LocalStorage, Rules, CODE_BLOCK_RULE } from \"@k-apps.io/llm-dsl\";\nimport { ChatGPT, Options } from \"@k-apps.io/llm-dsl-chatgpt\";\n\nrequire( \"dotenv\" ).config();\n\nconst main = () => {\n\n  const LLM = new ChatGPT({});\n\n  type Context = string[];\n\n  const chat = new DSL<Options, Context>( {\n    llm: LLM,\n    storage: LocalStorage,\n    options: {\n      model: \"gpt-4\"\n    }\n  } );\n\n  chat\n    .rule( CODE_BLOCK_RULE )\n    .prompt( {\n      message: \"Hello!\"\n    } )\n    .prompt( {\n      message: 'send me 3 progamming languages as a JSON object e.g ```json [\"lang1\", \"lang2\", \"langN\"]```',\n    } )\n    .expect( ( response ) => new Promise<void>( ( resolve, reject ) => {\n      // setting expectations for the response\n      if ( response.codeBlocks === undefined ) {\n        reject( \"expecting a json code block\" );\n      } else if ( response.codeBlocks.length > 1 ) {\n        reject( \"only 1 code block was requested\" );\n      } else {\n        const block = response.codeBlocks[ 0 ];\n        if ( block.lang !== \"json\" ) {\n          reject( `expecting a json code block, found '${ block.lang }' instead` );\n        } else {\n          const data = JSON.parse( block.code );\n          // setting the context in this command makes it available hereafter\n          chat.context = data;\n          resolve();\n        }\n      }\n    } ) )\n    .promptForEach( ( languages: Context ) => {\n      // using this context I can send more prompts\n      return languages.map( language => {\n        const message = `write me a hello world progamming in ${ language }`;\n        return { message: message };\n      } );\n    } )\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then( () => {\n      console.log( \"done\" );\n    } )\n    .catch( error => {\n      console.error( error );\n    } );\n}\n```\n\n# Rules\na `rule` represents a specific instruction for the LLM and `you` to follow. Regardless where you define these in the chain of commands they will always be included at the top of the convesation with `Visibility.REQUIRED`\n\n```javascript\nimport { DSL, Rule } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    .rule({\n      name: \"Bob Uecker\",\n      requirement: \"respond in the style of Bob Uecker, the Famous Radio Broadcaster and not so Fammous American League Baseball Player\"\n    })\n    .prompt({\n      message: \"Uecke, give me a home run call\"\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content );\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Functions\n\nIn your DSL script, you can define custom `functions` that the Language Model (LLM) may call, and the results will be returned as new `Message` objects. This feature allows you to create custom logic and interactions within your conversation with the LLM.\n\n```javascript\nimport { DSL, Rule, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    .function<{ unit?: \"Fahrenheit\" | \"Celsius\"; }>( {\n      name: \"getTheCurrentWeather\",\n      description: \"helps the AI model determine the current weather\",\n      parameters: {\n        \"type\": \"object\",\n        \"properties\": {\n          \"unit\": {\n            \"type\": \"string\",\n            \"enum\": [ \"Fahrenheit\", \"Celsius\" ]\n          }\n        }\n      },\n      func: ( { unit = \"Fahrenheit\", context } ) => {\n        return new Promise( ( resolve, reject ) => {\n          const weather = {\n            temperature: unit === \"Fahrenheit\" ? 24 : 5,\n            unit: unit,\n            percipitation: \"it's snowing\"\n          };\n          resolve( {\n            message: `here is the current weather report \\`\\`\\`json ${ JSON.stringify( weather ) }\\`\\`\\``\n          } );\n        } );\n      }\n    } )\n    .rule( {\n      name: \"Temperature\",\n      requirement: \"all temperature readings must be in Celsius\"\n    } )\n    .prompt( {\n      message: \"what's the weather like today?\"\n    } )\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content );\n    } )\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Prompting\na `prompt` is the main form of interacting with the LLM, use this command to send a message and generate a response\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL<Options, string>({});\n  chat.context = \"what is something the bank has that could be used on the football field?\";\n  chat\n    .prompt({\n      message: \"Why did the football team go to the bank?\"\n    })\n    .prompt( context => {\n      // a prompt can also access the context of the chat via a `CommandFunction`\n      return {\n        message: `here is a hint: ${context}`\n      }\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Context\n\nthe `context` is a shared space that enables the seamless exchange of information between different commands and prompts. It's a vital tool for tasks that involve gathering, adjusting, or retrieving data across multiple interactions. The `context` ensures your conversation flows cohesively and empowers you to manage and transfer artifacts effortlessly.\n\nTo manage the `context`, you can use commands like [response](#response) and [expect](#expect) or call `setContext` directly\n\nTo use the `context`, checkout [prompt](#prompting), [promptForEach](#promptforeach) and [branchForEach](#branchforeach)\n\n\n## response\n`response` provides direct accesss the response of a prompt, it gives you control to continue the chain of commands by calling `resolve` or stop by calling `reject`\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL<Options, string>({});\n  chat\n    .prompt({\n      message: \"finish the sentence 'one does not'\"\n    })\n    .response( response => new Promise<void>(( resolve, reject ) => {\n      if ( response.content.includes(\"Mordor\")) {\n        chat.context = {\n          isChill: true,\n        }\n      } else {\n        chat.context = {\n          isChill: false,\n          tasks: [ \"watch LOTR\" ]\n        }\n      }\n      resolve();\n    }))\n    .prompt( ( context, $this ) => {\n      if ( context.isChill ) {\n        return {\n          message: \"great answer\"\n        }\n      } else {\n        return {\n          message: \"I need you to watch LOTR\"\n        }\n      }\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n## expect\nThe `expect` function is another way to access the response of a prompt and set expectations for it. When working with the Language Model (LLM), you can use this command to define specific criteria that the response should meet. If these criteria are not met, you have the option to call `reject(\"with your reason\")` to initiate a [dispute resolution](#dispute-resolution) process. On the other hand, if the response aligns with your expectations, you can use the `resolve()` method to continue the chain of interactions.\n\nThis command plays a pivotal role in ensuring that the Language Model's responses are in line with your desired outcomes. It serves as a valuable tool for quality control and enables the creation of custom interactions with the model. By setting clear expectations and handling responses accordingly, you can shape the behavior of the model to meet your specific needs.\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\ninterface Club {\n  club: string;\n  yardage: number;\n}\n\nconst main = () => {\n  const chat = new DSL<Options, Club[]>({});\n  chat\n    .prompt({\n      message: \"please generate a JSON Array of golf clubs I could hit out of the ruff. This JSON array must structured as ```javascript {\\noptions: { club: string; yardage: number; }[]\\n}``` and be provided as a JSON code block\"\n    })\n    .expect( response => new Promise<void>(( resolve, reject ) => {\n      // I have high expecations\n      if ( response.codeBlocks === undefined ) {\n        reject(\"a json code block was expected\")\n      } else if ( response.codeBlocks.length > 1 ) {\n        reject(\"too many code blocks, only 1 is required\")\n      } else {\n        const block = response.codeBlocks[0];\n        if ( block.lang !== \"json\" ) {\n          reject(\"code block must be 'json'\")\n        } else {\n          const data = JSON.parse(block.code);\n          chat.context = data.options;\n          resolve();\n        }\n      }\n    }))\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n#### Dispute Resolution\n\nWhen you call `reject(\"with your reason\")`, the DSL will initiate a `sidebar` chat to resolve the dispute between your expectations and the prior response. This `sidebar` chat retains the `conetxt`, `rules` and `functions` you originally defined in the main chat.\n\nEach response generated in the `sidebar` will be assessed against your expectations until they are met or the maximum call stack limit is reached. \n\n> Adjust the expect max call stack with `setMaxCallStack`\n\nThe response that aligns with your expectations will be marked with `Visibility.OPTIONAL` and will follow the response that triggered the dispute. The initial response that led to the dispute will be updated with a visibility of `Visibility.HIDDEN`, and the chain of commands will resume.\n\n## promptForEach\nThe `promptForEach` command allows you to use the chat `context` to generate one or more prompts, each of which is executed sequentially and synchronously. This function is particularly useful when you need to create multiple prompts dynamically based on the context data.\n\n\n```javascript\nimport { DSL } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    /** continued from above */\n    .promptForEach( ( context: Club[] ) => {\n      return context.map(({ club, yardage}, index ) => {\n        return {\n          message: `I expect the ruff to take about 40% off the distance of the club, what is the new yardarge for ${club}?`\n        }\n      })\n    })\n    .prompt({\n      message: \"I have 120y to the hole and I want to carry it 115y, which club should I use?\"\n    })\n    .prompt({\n      message: \"would this be a full swing?\"\n    })\n    .prompt({\n      message: \"I stuck it close, let's get some birds\",\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n## branchForEach\n`branchForEach` similary enables you to use the chat `context` to generate a series of commands. The series is scoped to each command between the `branchForEach` and the next `join` command. Each series is executed sequentially and synchronously.\n\n```javascript\nimport { DSL } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const LLM = new ChatGPT( {} );\n\n  const chat = new DSL<PromptOptions, Context>( {\n    llm: LLM,\n    storage: LocalStorage,\n    options: {\n      model: \"gpt-4\",\n      temperature: 0.5,\n      top_p: 0.5\n    }\n  } );\n\n  chat\n    .rule( CODE_BLOCK_RULE )\n    .prompt( {\n      message: \"generate a list of cities implementing the ```typescript interface { cities: string[] }``` as a json code block\"\n    } )\n    .expect( response => {\n      // todo - set the expectations\n      const data = JSON.parse( response.codeBlocks![0].code );\n      chat.context = data.cities;\n      resolve();\n    })\n    .branchForEach( context => {\n      return context.map( city => {\n        return {\n          message: `consider the following city, ${ city } and pick a monument, artificat or famous person in this city we'll call this the target`\n        };\n      } );\n    } )\n    .prompt( {\n      message: \"consider the RPG table top game Sprawl, create a new mission in this city that involves our target\"\n    } )\n    .response( response => {\n      // write the response to a file\n      return new Promise<void>(( resolve, reject ) => {\n        writeFileSync(`./sprawl-missions/${response.id}.txt`);\n        resolve();\n      })\n    })\n    .join()\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then( () => {\n      console.log( \"done\" );\n    } )\n    .catch( error => {\n      console.error( error );\n    } );\n};\n\nmain();\n\n```\n\n# Storage\nchats can be stored for later use; this requires a `storage` mechanism. This mechanism manages saving and retrieving chats from a data store. This package includes `LocalStorage` which will save the chats to the local file system and `NoStorage` which does not save any chats. \n\n`LocalStorage` requires an environment variable `CHATS_DIR` where the chats will be stored as `{id}.json` files \n> CHATS_DIR will not be created automatically\n```text\n# .env\nCHATS_DIR=/opt/chats\n```\n\n\n## Custom\nyou can define your own storage mechanism by implementing the `Storage` interface\n\n```javascript\nimport { ChatStorage } from \"@k-apps.io/llm-dsl\";\n\nconst MyAPI: ChatStorage = {\n  getById: ( id: string ): Promise<Chat> => {\n    return new Promise<Chat>( ( resolve, reject ) => {\n      /**\n       * TODO: retrieve an existing chat\n       */\n      reject();\n    } );\n  },\n  save: ( chat: Chat ): Promise<void> => {\n    return new Promise<void>( ( resolve, reject ) => {\n      /**\n       * TODO: save the chat\n       */\n      reject();\n    } );\n  }\n}\n\nconst main = () => {\n  const chat = new DSL({\n    storage: MyAPI,\n    // ...\n  });\n}\n\nmain();\n```\n","readmeFilename":"README.md","gitHead":"720283da033efd537e8148cc23ff6af7301de04b","_id":"@k-apps.io/llm-dsl@1.2.1-beta.4","_nodeVersion":"18.13.0","_npmVersion":"9.2.0","dist":{"integrity":"sha512-8Xlbqo4IoWyV6JlJAURE707qThOGHEoA0vIQMysTp6F2QwtE/7OAQwf8aca1xpfH2mKCnLPxHt09AZOSHTByrw==","shasum":"668b8d8534070ed1dfefc0d8a8d40badf4bffbbf","tarball":"https://registry.npmjs.org/@k-apps.io/llm-dsl/-/llm-dsl-1.2.1-beta.4.tgz","fileCount":14,"unpackedSize":57544,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQClItMoxBm+ggcpp666Uc4k8iyikB2ZbQeIczi83ZSf7gIhAMjmEoQE54yF/QCYcTaQClnxzLRILR7111/vhveKzjjA"}]},"_npmUser":{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"},"directories":{},"maintainers":[{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/llm-dsl_1.2.1-beta.4_1700600755624_0.1574459853980521"},"_hasShrinkwrap":false},"1.2.1-beta.5":{"name":"@k-apps.io/llm-dsl","version":"1.2.1-beta.5","description":"a DSL for Large Language Models (LLM)","main":"dist/index.js","scripts":{"test":"jest","build":"tsc","deploy-prod":"npm run build && npm publish --access public","deploy":"npm run build && git add . && git commit -m \"Committing changes before beta deploy\" && npm version prerelease --preid=beta && npm publish --tag beta"},"repository":{"type":"git","url":"git+https://github.com/k-apps-io/llm-dsl.git"},"keywords":["LLM","chatgpt","openai"],"author":{"name":"@imnotakopp"},"license":"ISC","bugs":{"url":"https://github.com/k-apps-io/llm-dsl/issues"},"homepage":"https://github.com/k-apps-io/llm-dsl#readme","devDependencies":{"@k-apps.io/llm-dsl-chatgpt":"^1.2.1-beta.0","@types/jest":"^29.5.1","@types/lodash":"^4.14.200","@types/uuid":"^9.0.6","dotenv":"^16.3.1","jest":"^29.5.0","openai":"^4.14.2","tiktoken":"^1.0.10","ts-jest":"^29.1.0","ts-node":"^10.9.1","typescript":"^5.2.2"},"dependencies":{"ebnf":"^1.9.1","json5":"^2.2.3","lodash":"^4.17.21","uuid":"^9.0.1"},"types":"./dist/index.d.ts","readme":"# @k-apps.io/llm-dsl\nthis is a DSL for interacting with a LLM, chaining commands together to formulate robust tasks\n\n# Installation\n\n```shell\nnpm install @k-apps.io/llm-dsl\n```\n\npair this with an LLM implementation such as\n\n[@k-apps.io/llm-dsl-chatgpt](https://www.npmjs.com/package/@k-apps.io/llm-dsl-chatgpt)\n\n# Usage\n\n### Hello World\n```javascript\nimport { LLM, DSL, LocalStorage, Rules, CODE_BLOCK_RULE } from \"@k-apps.io/llm-dsl\";\nimport { ChatGPT, Options } from \"@k-apps.io/llm-dsl-chatgpt\";\n\nrequire( \"dotenv\" ).config();\n\nconst main = () => {\n\n  const LLM = new ChatGPT({});\n\n  type Context = string[];\n\n  const chat = new DSL<Options, Context>( {\n    llm: LLM,\n    storage: LocalStorage,\n    options: {\n      model: \"gpt-4\"\n    }\n  } );\n\n  chat\n    .rule( CODE_BLOCK_RULE )\n    .prompt( {\n      message: \"Hello!\"\n    } )\n    .prompt( {\n      message: 'send me 3 progamming languages as a JSON object e.g ```json [\"lang1\", \"lang2\", \"langN\"]```',\n    } )\n    .expect( ( response ) => new Promise<void>( ( resolve, reject ) => {\n      // setting expectations for the response\n      if ( response.codeBlocks === undefined ) {\n        reject( \"expecting a json code block\" );\n      } else if ( response.codeBlocks.length > 1 ) {\n        reject( \"only 1 code block was requested\" );\n      } else {\n        const block = response.codeBlocks[ 0 ];\n        if ( block.lang !== \"json\" ) {\n          reject( `expecting a json code block, found '${ block.lang }' instead` );\n        } else {\n          const data = JSON.parse( block.code );\n          // setting the context in this command makes it available hereafter\n          chat.context = data;\n          resolve();\n        }\n      }\n    } ) )\n    .promptForEach( ( languages: Context ) => {\n      // using this context I can send more prompts\n      return languages.map( language => {\n        const message = `write me a hello world progamming in ${ language }`;\n        return { message: message };\n      } );\n    } )\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then( () => {\n      console.log( \"done\" );\n    } )\n    .catch( error => {\n      console.error( error );\n    } );\n}\n```\n\n# Rules\na `rule` represents a specific instruction for the LLM and `you` to follow. Regardless where you define these in the chain of commands they will always be included at the top of the convesation with `Visibility.REQUIRED`\n\n```javascript\nimport { DSL, Rule } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    .rule({\n      name: \"Bob Uecker\",\n      requirement: \"respond in the style of Bob Uecker, the Famous Radio Broadcaster and not so Fammous American League Baseball Player\"\n    })\n    .prompt({\n      message: \"Uecke, give me a home run call\"\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content );\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Functions\n\nIn your DSL script, you can define custom `functions` that the Language Model (LLM) may call, and the results will be returned as new `Message` objects. This feature allows you to create custom logic and interactions within your conversation with the LLM.\n\n```javascript\nimport { DSL, Rule, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    .function<{ unit?: \"Fahrenheit\" | \"Celsius\"; }>( {\n      name: \"getTheCurrentWeather\",\n      description: \"helps the AI model determine the current weather\",\n      parameters: {\n        \"type\": \"object\",\n        \"properties\": {\n          \"unit\": {\n            \"type\": \"string\",\n            \"enum\": [ \"Fahrenheit\", \"Celsius\" ]\n          }\n        }\n      },\n      func: ( { unit = \"Fahrenheit\", context } ) => {\n        return new Promise( ( resolve, reject ) => {\n          const weather = {\n            temperature: unit === \"Fahrenheit\" ? 24 : 5,\n            unit: unit,\n            percipitation: \"it's snowing\"\n          };\n          resolve( {\n            message: `here is the current weather report \\`\\`\\`json ${ JSON.stringify( weather ) }\\`\\`\\``\n          } );\n        } );\n      }\n    } )\n    .rule( {\n      name: \"Temperature\",\n      requirement: \"all temperature readings must be in Celsius\"\n    } )\n    .prompt( {\n      message: \"what's the weather like today?\"\n    } )\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content );\n    } )\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Prompting\na `prompt` is the main form of interacting with the LLM, use this command to send a message and generate a response\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL<Options, string>({});\n  chat.context = \"what is something the bank has that could be used on the football field?\";\n  chat\n    .prompt({\n      message: \"Why did the football team go to the bank?\"\n    })\n    .prompt( context => {\n      // a prompt can also access the context of the chat via a `CommandFunction`\n      return {\n        message: `here is a hint: ${context}`\n      }\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Context\n\nthe `context` is a shared space that enables the seamless exchange of information between different commands and prompts. It's a vital tool for tasks that involve gathering, adjusting, or retrieving data across multiple interactions. The `context` ensures your conversation flows cohesively and empowers you to manage and transfer artifacts effortlessly.\n\nTo manage the `context`, you can use commands like [response](#response) and [expect](#expect) or call `setContext` directly\n\nTo use the `context`, checkout [prompt](#prompting), [promptForEach](#promptforeach) and [branchForEach](#branchforeach)\n\n\n## response\n`response` provides direct accesss the response of a prompt, it gives you control to continue the chain of commands by calling `resolve` or stop by calling `reject`\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL<Options, string>({});\n  chat\n    .prompt({\n      message: \"finish the sentence 'one does not'\"\n    })\n    .response( response => new Promise<void>(( resolve, reject ) => {\n      if ( response.content.includes(\"Mordor\")) {\n        chat.context = {\n          isChill: true,\n        }\n      } else {\n        chat.context = {\n          isChill: false,\n          tasks: [ \"watch LOTR\" ]\n        }\n      }\n      resolve();\n    }))\n    .prompt( ( context, $this ) => {\n      if ( context.isChill ) {\n        return {\n          message: \"great answer\"\n        }\n      } else {\n        return {\n          message: \"I need you to watch LOTR\"\n        }\n      }\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n## expect\nThe `expect` function is another way to access the response of a prompt and set expectations for it. When working with the Language Model (LLM), you can use this command to define specific criteria that the response should meet. If these criteria are not met, you have the option to call `reject(\"with your reason\")` to initiate a [dispute resolution](#dispute-resolution) process. On the other hand, if the response aligns with your expectations, you can use the `resolve()` method to continue the chain of interactions.\n\nThis command plays a pivotal role in ensuring that the Language Model's responses are in line with your desired outcomes. It serves as a valuable tool for quality control and enables the creation of custom interactions with the model. By setting clear expectations and handling responses accordingly, you can shape the behavior of the model to meet your specific needs.\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\ninterface Club {\n  club: string;\n  yardage: number;\n}\n\nconst main = () => {\n  const chat = new DSL<Options, Club[]>({});\n  chat\n    .prompt({\n      message: \"please generate a JSON Array of golf clubs I could hit out of the ruff. This JSON array must structured as ```javascript {\\noptions: { club: string; yardage: number; }[]\\n}``` and be provided as a JSON code block\"\n    })\n    .expect( response => new Promise<void>(( resolve, reject ) => {\n      // I have high expecations\n      if ( response.codeBlocks === undefined ) {\n        reject(\"a json code block was expected\")\n      } else if ( response.codeBlocks.length > 1 ) {\n        reject(\"too many code blocks, only 1 is required\")\n      } else {\n        const block = response.codeBlocks[0];\n        if ( block.lang !== \"json\" ) {\n          reject(\"code block must be 'json'\")\n        } else {\n          const data = JSON.parse(block.code);\n          chat.context = data.options;\n          resolve();\n        }\n      }\n    }))\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n#### Dispute Resolution\n\nWhen you call `reject(\"with your reason\")`, the DSL will initiate a `sidebar` chat to resolve the dispute between your expectations and the prior response. This `sidebar` chat retains the `conetxt`, `rules` and `functions` you originally defined in the main chat.\n\nEach response generated in the `sidebar` will be assessed against your expectations until they are met or the maximum call stack limit is reached. \n\n> Adjust the expect max call stack with `setMaxCallStack`\n\nThe response that aligns with your expectations will be marked with `Visibility.OPTIONAL` and will follow the response that triggered the dispute. The initial response that led to the dispute will be updated with a visibility of `Visibility.HIDDEN`, and the chain of commands will resume.\n\n## promptForEach\nThe `promptForEach` command allows you to use the chat `context` to generate one or more prompts, each of which is executed sequentially and synchronously. This function is particularly useful when you need to create multiple prompts dynamically based on the context data.\n\n\n```javascript\nimport { DSL } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    /** continued from above */\n    .promptForEach( ( context: Club[] ) => {\n      return context.map(({ club, yardage}, index ) => {\n        return {\n          message: `I expect the ruff to take about 40% off the distance of the club, what is the new yardarge for ${club}?`\n        }\n      })\n    })\n    .prompt({\n      message: \"I have 120y to the hole and I want to carry it 115y, which club should I use?\"\n    })\n    .prompt({\n      message: \"would this be a full swing?\"\n    })\n    .prompt({\n      message: \"I stuck it close, let's get some birds\",\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n## branchForEach\n`branchForEach` similary enables you to use the chat `context` to generate a series of commands. The series is scoped to each command between the `branchForEach` and the next `join` command. Each series is executed sequentially and synchronously.\n\n```javascript\nimport { DSL } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const LLM = new ChatGPT( {} );\n\n  const chat = new DSL<PromptOptions, Context>( {\n    llm: LLM,\n    storage: LocalStorage,\n    options: {\n      model: \"gpt-4\",\n      temperature: 0.5,\n      top_p: 0.5\n    }\n  } );\n\n  chat\n    .rule( CODE_BLOCK_RULE )\n    .prompt( {\n      message: \"generate a list of cities implementing the ```typescript interface { cities: string[] }``` as a json code block\"\n    } )\n    .expect( response => {\n      // todo - set the expectations\n      const data = JSON.parse( response.codeBlocks![0].code );\n      chat.context = data.cities;\n      resolve();\n    })\n    .branchForEach( context => {\n      return context.map( city => {\n        return {\n          message: `consider the following city, ${ city } and pick a monument, artificat or famous person in this city we'll call this the target`\n        };\n      } );\n    } )\n    .prompt( {\n      message: \"consider the RPG table top game Sprawl, create a new mission in this city that involves our target\"\n    } )\n    .response( response => {\n      // write the response to a file\n      return new Promise<void>(( resolve, reject ) => {\n        writeFileSync(`./sprawl-missions/${response.id}.txt`);\n        resolve();\n      })\n    })\n    .join()\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then( () => {\n      console.log( \"done\" );\n    } )\n    .catch( error => {\n      console.error( error );\n    } );\n};\n\nmain();\n\n```\n\n# Storage\nchats can be stored for later use; this requires a `storage` mechanism. This mechanism manages saving and retrieving chats from a data store. This package includes `LocalStorage` which will save the chats to the local file system and `NoStorage` which does not save any chats. \n\n`LocalStorage` requires an environment variable `CHATS_DIR` where the chats will be stored as `{id}.json` files \n> CHATS_DIR will not be created automatically\n```text\n# .env\nCHATS_DIR=/opt/chats\n```\n\n\n## Custom\nyou can define your own storage mechanism by implementing the `Storage` interface\n\n```javascript\nimport { ChatStorage } from \"@k-apps.io/llm-dsl\";\n\nconst MyAPI: ChatStorage = {\n  getById: ( id: string ): Promise<Chat> => {\n    return new Promise<Chat>( ( resolve, reject ) => {\n      /**\n       * TODO: retrieve an existing chat\n       */\n      reject();\n    } );\n  },\n  save: ( chat: Chat ): Promise<void> => {\n    return new Promise<void>( ( resolve, reject ) => {\n      /**\n       * TODO: save the chat\n       */\n      reject();\n    } );\n  }\n}\n\nconst main = () => {\n  const chat = new DSL({\n    storage: MyAPI,\n    // ...\n  });\n}\n\nmain();\n```\n","readmeFilename":"README.md","gitHead":"574d5a57f338f6dec417d9cc1647c18d08cc3109","_id":"@k-apps.io/llm-dsl@1.2.1-beta.5","_nodeVersion":"18.13.0","_npmVersion":"9.2.0","dist":{"integrity":"sha512-ijuWo6EEft9Afb6G7Lw0kVS/imiDl0gscyu+1alcbdlqJEahBeCM++Nogq3nuDwIA2F9KcXG9x/kpk61dsNJ2Q==","shasum":"144ae647f17cf8ad851f7896adb9d3a7b669f746","tarball":"https://registry.npmjs.org/@k-apps.io/llm-dsl/-/llm-dsl-1.2.1-beta.5.tgz","fileCount":14,"unpackedSize":57581,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCHtsFETyAyHh4NWU5z9XfoAiPmPqn15KvrsVdEJnMM0wIgHiIt7r/k7YTRkmC6MsBG9DIvUwlGHEmJu/JETwpHZiw="}]},"_npmUser":{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"},"directories":{},"maintainers":[{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/llm-dsl_1.2.1-beta.5_1700602115445_0.6825405816321102"},"_hasShrinkwrap":false},"1.2.1-beta.6":{"name":"@k-apps.io/llm-dsl","version":"1.2.1-beta.6","description":"a DSL for Large Language Models (LLM)","main":"dist/index.js","scripts":{"test":"jest","build":"tsc","deploy-prod":"npm run build && npm publish --access public","deploy":"npm run build && git add . && git commit -m \"Committing changes before beta deploy\" && npm version prerelease --preid=beta && npm publish --tag beta"},"repository":{"type":"git","url":"git+https://github.com/k-apps-io/llm-dsl.git"},"keywords":["LLM","chatgpt","openai"],"author":{"name":"@imnotakopp"},"license":"ISC","bugs":{"url":"https://github.com/k-apps-io/llm-dsl/issues"},"homepage":"https://github.com/k-apps-io/llm-dsl#readme","devDependencies":{"@k-apps.io/llm-dsl-chatgpt":"^1.2.1-beta.0","@types/jest":"^29.5.1","@types/lodash":"^4.14.200","@types/uuid":"^9.0.6","dotenv":"^16.3.1","jest":"^29.5.0","openai":"^4.14.2","tiktoken":"^1.0.10","ts-jest":"^29.1.0","ts-node":"^10.9.1","typescript":"^5.2.2"},"dependencies":{"ebnf":"^1.9.1","json5":"^2.2.3","lodash":"^4.17.21","uuid":"^9.0.1"},"types":"./dist/index.d.ts","readme":"# @k-apps.io/llm-dsl\nthis is a DSL for interacting with a LLM, chaining commands together to formulate robust tasks\n\n# Installation\n\n```shell\nnpm install @k-apps.io/llm-dsl\n```\n\npair this with an LLM implementation such as\n\n[@k-apps.io/llm-dsl-chatgpt](https://www.npmjs.com/package/@k-apps.io/llm-dsl-chatgpt)\n\n# Usage\n\n### Hello World\n```javascript\nimport { LLM, DSL, LocalStorage, Rules, CODE_BLOCK_RULE } from \"@k-apps.io/llm-dsl\";\nimport { ChatGPT, Options } from \"@k-apps.io/llm-dsl-chatgpt\";\n\nrequire( \"dotenv\" ).config();\n\nconst main = () => {\n\n  const LLM = new ChatGPT({});\n\n  type Context = string[];\n\n  const chat = new DSL<Options, Context>( {\n    llm: LLM,\n    storage: LocalStorage,\n    options: {\n      model: \"gpt-4\"\n    }\n  } );\n\n  chat\n    .rule( CODE_BLOCK_RULE )\n    .prompt( {\n      message: \"Hello!\"\n    } )\n    .prompt( {\n      message: 'send me 3 progamming languages as a JSON object e.g ```json [\"lang1\", \"lang2\", \"langN\"]```',\n    } )\n    .expect( ( response ) => new Promise<void>( ( resolve, reject ) => {\n      // setting expectations for the response\n      if ( response.codeBlocks === undefined ) {\n        reject( \"expecting a json code block\" );\n      } else if ( response.codeBlocks.length > 1 ) {\n        reject( \"only 1 code block was requested\" );\n      } else {\n        const block = response.codeBlocks[ 0 ];\n        if ( block.lang !== \"json\" ) {\n          reject( `expecting a json code block, found '${ block.lang }' instead` );\n        } else {\n          const data = JSON.parse( block.code );\n          // setting the context in this command makes it available hereafter\n          chat.context = data;\n          resolve();\n        }\n      }\n    } ) )\n    .promptForEach( ( languages: Context ) => {\n      // using this context I can send more prompts\n      return languages.map( language => {\n        const message = `write me a hello world progamming in ${ language }`;\n        return { message: message };\n      } );\n    } )\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then( () => {\n      console.log( \"done\" );\n    } )\n    .catch( error => {\n      console.error( error );\n    } );\n}\n```\n\n# Rules\na `rule` represents a specific instruction for the LLM and `you` to follow. Regardless where you define these in the chain of commands they will always be included at the top of the convesation with `Visibility.REQUIRED`\n\n```javascript\nimport { DSL, Rule } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    .rule({\n      name: \"Bob Uecker\",\n      requirement: \"respond in the style of Bob Uecker, the Famous Radio Broadcaster and not so Fammous American League Baseball Player\"\n    })\n    .prompt({\n      message: \"Uecke, give me a home run call\"\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content );\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Functions\n\nIn your DSL script, you can define custom `functions` that the Language Model (LLM) may call, and the results will be returned as new `Message` objects. This feature allows you to create custom logic and interactions within your conversation with the LLM.\n\n```javascript\nimport { DSL, Rule, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    .function<{ unit?: \"Fahrenheit\" | \"Celsius\"; }>( {\n      name: \"getTheCurrentWeather\",\n      description: \"helps the AI model determine the current weather\",\n      parameters: {\n        \"type\": \"object\",\n        \"properties\": {\n          \"unit\": {\n            \"type\": \"string\",\n            \"enum\": [ \"Fahrenheit\", \"Celsius\" ]\n          }\n        }\n      },\n      func: ( { unit = \"Fahrenheit\", context } ) => {\n        return new Promise( ( resolve, reject ) => {\n          const weather = {\n            temperature: unit === \"Fahrenheit\" ? 24 : 5,\n            unit: unit,\n            percipitation: \"it's snowing\"\n          };\n          resolve( {\n            message: `here is the current weather report \\`\\`\\`json ${ JSON.stringify( weather ) }\\`\\`\\``\n          } );\n        } );\n      }\n    } )\n    .rule( {\n      name: \"Temperature\",\n      requirement: \"all temperature readings must be in Celsius\"\n    } )\n    .prompt( {\n      message: \"what's the weather like today?\"\n    } )\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content );\n    } )\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Prompting\na `prompt` is the main form of interacting with the LLM, use this command to send a message and generate a response\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL<Options, string>({});\n  chat.context = \"what is something the bank has that could be used on the football field?\";\n  chat\n    .prompt({\n      message: \"Why did the football team go to the bank?\"\n    })\n    .prompt( context => {\n      // a prompt can also access the context of the chat via a `CommandFunction`\n      return {\n        message: `here is a hint: ${context}`\n      }\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Context\n\nthe `context` is a shared space that enables the seamless exchange of information between different commands and prompts. It's a vital tool for tasks that involve gathering, adjusting, or retrieving data across multiple interactions. The `context` ensures your conversation flows cohesively and empowers you to manage and transfer artifacts effortlessly.\n\nTo manage the `context`, you can use commands like [response](#response) and [expect](#expect) or call `setContext` directly\n\nTo use the `context`, checkout [prompt](#prompting), [promptForEach](#promptforeach) and [branchForEach](#branchforeach)\n\n\n## response\n`response` provides direct accesss the response of a prompt, it gives you control to continue the chain of commands by calling `resolve` or stop by calling `reject`\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL<Options, string>({});\n  chat\n    .prompt({\n      message: \"finish the sentence 'one does not'\"\n    })\n    .response( response => new Promise<void>(( resolve, reject ) => {\n      if ( response.content.includes(\"Mordor\")) {\n        chat.context = {\n          isChill: true,\n        }\n      } else {\n        chat.context = {\n          isChill: false,\n          tasks: [ \"watch LOTR\" ]\n        }\n      }\n      resolve();\n    }))\n    .prompt( ( context, $this ) => {\n      if ( context.isChill ) {\n        return {\n          message: \"great answer\"\n        }\n      } else {\n        return {\n          message: \"I need you to watch LOTR\"\n        }\n      }\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n## expect\nThe `expect` function is another way to access the response of a prompt and set expectations for it. When working with the Language Model (LLM), you can use this command to define specific criteria that the response should meet. If these criteria are not met, you have the option to call `reject(\"with your reason\")` to initiate a [dispute resolution](#dispute-resolution) process. On the other hand, if the response aligns with your expectations, you can use the `resolve()` method to continue the chain of interactions.\n\nThis command plays a pivotal role in ensuring that the Language Model's responses are in line with your desired outcomes. It serves as a valuable tool for quality control and enables the creation of custom interactions with the model. By setting clear expectations and handling responses accordingly, you can shape the behavior of the model to meet your specific needs.\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\ninterface Club {\n  club: string;\n  yardage: number;\n}\n\nconst main = () => {\n  const chat = new DSL<Options, Club[]>({});\n  chat\n    .prompt({\n      message: \"please generate a JSON Array of golf clubs I could hit out of the ruff. This JSON array must structured as ```javascript {\\noptions: { club: string; yardage: number; }[]\\n}``` and be provided as a JSON code block\"\n    })\n    .expect( response => new Promise<void>(( resolve, reject ) => {\n      // I have high expecations\n      if ( response.codeBlocks === undefined ) {\n        reject(\"a json code block was expected\")\n      } else if ( response.codeBlocks.length > 1 ) {\n        reject(\"too many code blocks, only 1 is required\")\n      } else {\n        const block = response.codeBlocks[0];\n        if ( block.lang !== \"json\" ) {\n          reject(\"code block must be 'json'\")\n        } else {\n          const data = JSON.parse(block.code);\n          chat.context = data.options;\n          resolve();\n        }\n      }\n    }))\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n#### Dispute Resolution\n\nWhen you call `reject(\"with your reason\")`, the DSL will initiate a `sidebar` chat to resolve the dispute between your expectations and the prior response. This `sidebar` chat retains the `conetxt`, `rules` and `functions` you originally defined in the main chat.\n\nEach response generated in the `sidebar` will be assessed against your expectations until they are met or the maximum call stack limit is reached. \n\n> Adjust the expect max call stack with `setMaxCallStack`\n\nThe response that aligns with your expectations will be marked with `Visibility.OPTIONAL` and will follow the response that triggered the dispute. The initial response that led to the dispute will be updated with a visibility of `Visibility.HIDDEN`, and the chain of commands will resume.\n\n## promptForEach\nThe `promptForEach` command allows you to use the chat `context` to generate one or more prompts, each of which is executed sequentially and synchronously. This function is particularly useful when you need to create multiple prompts dynamically based on the context data.\n\n\n```javascript\nimport { DSL } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    /** continued from above */\n    .promptForEach( ( context: Club[] ) => {\n      return context.map(({ club, yardage}, index ) => {\n        return {\n          message: `I expect the ruff to take about 40% off the distance of the club, what is the new yardarge for ${club}?`\n        }\n      })\n    })\n    .prompt({\n      message: \"I have 120y to the hole and I want to carry it 115y, which club should I use?\"\n    })\n    .prompt({\n      message: \"would this be a full swing?\"\n    })\n    .prompt({\n      message: \"I stuck it close, let's get some birds\",\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n## branchForEach\n`branchForEach` similary enables you to use the chat `context` to generate a series of commands. The series is scoped to each command between the `branchForEach` and the next `join` command. Each series is executed sequentially and synchronously.\n\n```javascript\nimport { DSL } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const LLM = new ChatGPT( {} );\n\n  const chat = new DSL<PromptOptions, Context>( {\n    llm: LLM,\n    storage: LocalStorage,\n    options: {\n      model: \"gpt-4\",\n      temperature: 0.5,\n      top_p: 0.5\n    }\n  } );\n\n  chat\n    .rule( CODE_BLOCK_RULE )\n    .prompt( {\n      message: \"generate a list of cities implementing the ```typescript interface { cities: string[] }``` as a json code block\"\n    } )\n    .expect( response => {\n      // todo - set the expectations\n      const data = JSON.parse( response.codeBlocks![0].code );\n      chat.context = data.cities;\n      resolve();\n    })\n    .branchForEach( context => {\n      return context.map( city => {\n        return {\n          message: `consider the following city, ${ city } and pick a monument, artificat or famous person in this city we'll call this the target`\n        };\n      } );\n    } )\n    .prompt( {\n      message: \"consider the RPG table top game Sprawl, create a new mission in this city that involves our target\"\n    } )\n    .response( response => {\n      // write the response to a file\n      return new Promise<void>(( resolve, reject ) => {\n        writeFileSync(`./sprawl-missions/${response.id}.txt`);\n        resolve();\n      })\n    })\n    .join()\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then( () => {\n      console.log( \"done\" );\n    } )\n    .catch( error => {\n      console.error( error );\n    } );\n};\n\nmain();\n\n```\n\n# Storage\nchats can be stored for later use; this requires a `storage` mechanism. This mechanism manages saving and retrieving chats from a data store. This package includes `LocalStorage` which will save the chats to the local file system and `NoStorage` which does not save any chats. \n\n`LocalStorage` requires an environment variable `CHATS_DIR` where the chats will be stored as `{id}.json` files \n> CHATS_DIR will not be created automatically\n```text\n# .env\nCHATS_DIR=/opt/chats\n```\n\n\n## Custom\nyou can define your own storage mechanism by implementing the `Storage` interface\n\n```javascript\nimport { ChatStorage } from \"@k-apps.io/llm-dsl\";\n\nconst MyAPI: ChatStorage = {\n  getById: ( id: string ): Promise<Chat> => {\n    return new Promise<Chat>( ( resolve, reject ) => {\n      /**\n       * TODO: retrieve an existing chat\n       */\n      reject();\n    } );\n  },\n  save: ( chat: Chat ): Promise<void> => {\n    return new Promise<void>( ( resolve, reject ) => {\n      /**\n       * TODO: save the chat\n       */\n      reject();\n    } );\n  }\n}\n\nconst main = () => {\n  const chat = new DSL({\n    storage: MyAPI,\n    // ...\n  });\n}\n\nmain();\n```\n","readmeFilename":"README.md","gitHead":"bc316c9e4148604e5ccdfff0a1629ce8b04aa051","_id":"@k-apps.io/llm-dsl@1.2.1-beta.6","_nodeVersion":"18.13.0","_npmVersion":"9.2.0","dist":{"integrity":"sha512-xfOxlcmDLi4Q1TsO4cuoXS+rprOM20YWI/fH4vBTNBXc6xQ6RretHC9ja2IH31cfkt0HP2mnu2nVdnhCGsSIBA==","shasum":"72a57f831ca56059efeac6c91f07d418603348d0","tarball":"https://registry.npmjs.org/@k-apps.io/llm-dsl/-/llm-dsl-1.2.1-beta.6.tgz","fileCount":14,"unpackedSize":57549,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIG1rKP0NvhK309LS10xcU7OJVyz0lS+Xn8TG/QtIXm3aAiASmWdupdPcTiGKBhkf2QMkddYVgUsFIvsOy+RCYzdf2g=="}]},"_npmUser":{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"},"directories":{},"maintainers":[{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/llm-dsl_1.2.1-beta.6_1700604949044_0.964380553401397"},"_hasShrinkwrap":false},"1.2.1-beta.7":{"name":"@k-apps.io/llm-dsl","version":"1.2.1-beta.7","description":"a DSL for Large Language Models (LLM)","main":"dist/index.js","scripts":{"test":"jest","build":"tsc","deploy-prod":"npm run build && npm publish --access public","deploy":"npm run build && git add . && git commit -m \"Committing changes before beta deploy\" && npm version prerelease --preid=beta && npm publish --tag beta"},"repository":{"type":"git","url":"git+https://github.com/k-apps-io/llm-dsl.git"},"keywords":["LLM","chatgpt","openai"],"author":{"name":"@imnotakopp"},"license":"ISC","bugs":{"url":"https://github.com/k-apps-io/llm-dsl/issues"},"homepage":"https://github.com/k-apps-io/llm-dsl#readme","devDependencies":{"@k-apps.io/llm-dsl-chatgpt":"^1.2.1-beta.0","@types/jest":"^29.5.1","@types/lodash":"^4.14.200","@types/uuid":"^9.0.6","dotenv":"^16.3.1","jest":"^29.5.0","openai":"^4.14.2","tiktoken":"^1.0.10","ts-jest":"^29.1.0","ts-node":"^10.9.1","typescript":"^5.2.2"},"dependencies":{"ebnf":"^1.9.1","json5":"^2.2.3","lodash":"^4.17.21","uuid":"^9.0.1"},"types":"./dist/index.d.ts","readme":"# todo's\nalign the CommandFunction arguments as a spreadable object\nupdate functions to resolve a string and set the role / visibility to System\nhave a stop call which resolves to then\nhave a condition series like branch\nbranch into sidebars for concurrent processing?\nload a module of functions to add to the chat\n\n# @k-apps.io/llm-dsl\nthis is a DSL for interacting with a LLM, chaining commands together to formulate robust tasks\n\n# Installation\n\n```shell\nnpm install @k-apps.io/llm-dsl\n```\n\npair this with an LLM implementation such as\n\n[@k-apps.io/llm-dsl-chatgpt](https://www.npmjs.com/package/@k-apps.io/llm-dsl-chatgpt)\n\n# Usage\n\n### Hello World\n```javascript\nimport { LLM, DSL, LocalStorage, Rules, CODE_BLOCK_RULE } from \"@k-apps.io/llm-dsl\";\nimport { ChatGPT, Options } from \"@k-apps.io/llm-dsl-chatgpt\";\n\nrequire( \"dotenv\" ).config();\n\nconst main = () => {\n\n  const LLM = new ChatGPT({});\n\n  type Context = string[];\n\n  const chat = new DSL<Options, Context>( {\n    llm: LLM,\n    storage: LocalStorage,\n    options: {\n      model: \"gpt-4\"\n    }\n  } );\n\n  chat\n    .rule( CODE_BLOCK_RULE )\n    .prompt( {\n      message: \"Hello!\"\n    } )\n    .prompt( {\n      message: 'send me 3 progamming languages as a JSON object e.g ```json [\"lang1\", \"lang2\", \"langN\"]```',\n    } )\n    .expect( ( response ) => new Promise<void>( ( resolve, reject ) => {\n      // setting expectations for the response\n      if ( response.codeBlocks === undefined ) {\n        reject( \"expecting a json code block\" );\n      } else if ( response.codeBlocks.length > 1 ) {\n        reject( \"only 1 code block was requested\" );\n      } else {\n        const block = response.codeBlocks[ 0 ];\n        if ( block.lang !== \"json\" ) {\n          reject( `expecting a json code block, found '${ block.lang }' instead` );\n        } else {\n          const data = JSON.parse( block.code );\n          // setting the context in this command makes it available hereafter\n          chat.context = data;\n          resolve();\n        }\n      }\n    } ) )\n    .promptForEach( ( languages: Context ) => {\n      // using this context I can send more prompts\n      return languages.map( language => {\n        const message = `write me a hello world progamming in ${ language }`;\n        return { message: message };\n      } );\n    } )\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then( () => {\n      console.log( \"done\" );\n    } )\n    .catch( error => {\n      console.error( error );\n    } );\n}\n```\n\n# Rules\na `rule` represents a specific instruction for the LLM and `you` to follow. Regardless where you define these in the chain of commands they will always be included at the top of the convesation with `Visibility.REQUIRED`\n\n```javascript\nimport { DSL, Rule } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    .rule({\n      name: \"Bob Uecker\",\n      requirement: \"respond in the style of Bob Uecker, the Famous Radio Broadcaster and not so Fammous American League Baseball Player\"\n    })\n    .prompt({\n      message: \"Uecke, give me a home run call\"\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content );\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Functions\n\nIn your DSL script, you can define custom `functions` that the Language Model (LLM) may call, and the results will be returned as new `Message` objects. This feature allows you to create custom logic and interactions within your conversation with the LLM.\n\n```javascript\nimport { DSL, Rule, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    .function<{ unit?: \"Fahrenheit\" | \"Celsius\"; }>( {\n      name: \"getTheCurrentWeather\",\n      description: \"helps the AI model determine the current weather\",\n      parameters: {\n        \"type\": \"object\",\n        \"properties\": {\n          \"unit\": {\n            \"type\": \"string\",\n            \"enum\": [ \"Fahrenheit\", \"Celsius\" ]\n          }\n        }\n      },\n      func: ( { unit = \"Fahrenheit\", context } ) => {\n        return new Promise( ( resolve, reject ) => {\n          const weather = {\n            temperature: unit === \"Fahrenheit\" ? 24 : 5,\n            unit: unit,\n            percipitation: \"it's snowing\"\n          };\n          resolve( {\n            message: `here is the current weather report \\`\\`\\`json ${ JSON.stringify( weather ) }\\`\\`\\``\n          } );\n        } );\n      }\n    } )\n    .rule( {\n      name: \"Temperature\",\n      requirement: \"all temperature readings must be in Celsius\"\n    } )\n    .prompt( {\n      message: \"what's the weather like today?\"\n    } )\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content );\n    } )\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Prompting\na `prompt` is the main form of interacting with the LLM, use this command to send a message and generate a response\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL<Options, string>({});\n  chat.context = \"what is something the bank has that could be used on the football field?\";\n  chat\n    .prompt({\n      message: \"Why did the football team go to the bank?\"\n    })\n    .prompt( context => {\n      // a prompt can also access the context of the chat via a `CommandFunction`\n      return {\n        message: `here is a hint: ${context}`\n      }\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Context\n\nthe `context` is a shared space that enables the seamless exchange of information between different commands and prompts. It's a vital tool for tasks that involve gathering, adjusting, or retrieving data across multiple interactions. The `context` ensures your conversation flows cohesively and empowers you to manage and transfer artifacts effortlessly.\n\nTo manage the `context`, you can use commands like [response](#response) and [expect](#expect) or call `setContext` directly\n\nTo use the `context`, checkout [prompt](#prompting), [promptForEach](#promptforeach) and [branchForEach](#branchforeach)\n\n\n## response\n`response` provides direct accesss the response of a prompt, it gives you control to continue the chain of commands by calling `resolve` or stop by calling `reject`\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL<Options, string>({});\n  chat\n    .prompt({\n      message: \"finish the sentence 'one does not'\"\n    })\n    .response( response => new Promise<void>(( resolve, reject ) => {\n      if ( response.content.includes(\"Mordor\")) {\n        chat.context = {\n          isChill: true,\n        }\n      } else {\n        chat.context = {\n          isChill: false,\n          tasks: [ \"watch LOTR\" ]\n        }\n      }\n      resolve();\n    }))\n    .prompt( ( context, $this ) => {\n      if ( context.isChill ) {\n        return {\n          message: \"great answer\"\n        }\n      } else {\n        return {\n          message: \"I need you to watch LOTR\"\n        }\n      }\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n## expect\nThe `expect` function is another way to access the response of a prompt and set expectations for it. When working with the Language Model (LLM), you can use this command to define specific criteria that the response should meet. If these criteria are not met, you have the option to call `reject(\"with your reason\")` to initiate a [dispute resolution](#dispute-resolution) process. On the other hand, if the response aligns with your expectations, you can use the `resolve()` method to continue the chain of interactions.\n\nThis command plays a pivotal role in ensuring that the Language Model's responses are in line with your desired outcomes. It serves as a valuable tool for quality control and enables the creation of custom interactions with the model. By setting clear expectations and handling responses accordingly, you can shape the behavior of the model to meet your specific needs.\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\ninterface Club {\n  club: string;\n  yardage: number;\n}\n\nconst main = () => {\n  const chat = new DSL<Options, Club[]>({});\n  chat\n    .prompt({\n      message: \"please generate a JSON Array of golf clubs I could hit out of the ruff. This JSON array must structured as ```javascript {\\noptions: { club: string; yardage: number; }[]\\n}``` and be provided as a JSON code block\"\n    })\n    .expect( response => new Promise<void>(( resolve, reject ) => {\n      // I have high expecations\n      if ( response.codeBlocks === undefined ) {\n        reject(\"a json code block was expected\")\n      } else if ( response.codeBlocks.length > 1 ) {\n        reject(\"too many code blocks, only 1 is required\")\n      } else {\n        const block = response.codeBlocks[0];\n        if ( block.lang !== \"json\" ) {\n          reject(\"code block must be 'json'\")\n        } else {\n          const data = JSON.parse(block.code);\n          chat.context = data.options;\n          resolve();\n        }\n      }\n    }))\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n#### Dispute Resolution\n\nWhen you call `reject(\"with your reason\")`, the DSL will initiate a `sidebar` chat to resolve the dispute between your expectations and the prior response. This `sidebar` chat retains the `conetxt`, `rules` and `functions` you originally defined in the main chat.\n\nEach response generated in the `sidebar` will be assessed against your expectations until they are met or the maximum call stack limit is reached. \n\n> Adjust the expect max call stack with `setMaxCallStack`\n\nThe response that aligns with your expectations will be marked with `Visibility.OPTIONAL` and will follow the response that triggered the dispute. The initial response that led to the dispute will be updated with a visibility of `Visibility.HIDDEN`, and the chain of commands will resume.\n\n## promptForEach\nThe `promptForEach` command allows you to use the chat `context` to generate one or more prompts, each of which is executed sequentially and synchronously. This function is particularly useful when you need to create multiple prompts dynamically based on the context data.\n\n\n```javascript\nimport { DSL } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    /** continued from above */\n    .promptForEach( ( context: Club[] ) => {\n      return context.map(({ club, yardage}, index ) => {\n        return {\n          message: `I expect the ruff to take about 40% off the distance of the club, what is the new yardarge for ${club}?`\n        }\n      })\n    })\n    .prompt({\n      message: \"I have 120y to the hole and I want to carry it 115y, which club should I use?\"\n    })\n    .prompt({\n      message: \"would this be a full swing?\"\n    })\n    .prompt({\n      message: \"I stuck it close, let's get some birds\",\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n## branchForEach\n`branchForEach` similary enables you to use the chat `context` to generate a series of commands. The series is scoped to each command between the `branchForEach` and the next `join` command. Each series is executed sequentially and synchronously.\n\n```javascript\nimport { DSL } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const LLM = new ChatGPT( {} );\n\n  const chat = new DSL<PromptOptions, Context>( {\n    llm: LLM,\n    storage: LocalStorage,\n    options: {\n      model: \"gpt-4\",\n      temperature: 0.5,\n      top_p: 0.5\n    }\n  } );\n\n  chat\n    .rule( CODE_BLOCK_RULE )\n    .prompt( {\n      message: \"generate a list of cities implementing the ```typescript interface { cities: string[] }``` as a json code block\"\n    } )\n    .expect( response => {\n      // todo - set the expectations\n      const data = JSON.parse( response.codeBlocks![0].code );\n      chat.context = data.cities;\n      resolve();\n    })\n    .branchForEach( context => {\n      return context.map( city => {\n        return {\n          message: `consider the following city, ${ city } and pick a monument, artificat or famous person in this city we'll call this the target`\n        };\n      } );\n    } )\n    .prompt( {\n      message: \"consider the RPG table top game Sprawl, create a new mission in this city that involves our target\"\n    } )\n    .response( response => {\n      // write the response to a file\n      return new Promise<void>(( resolve, reject ) => {\n        writeFileSync(`./sprawl-missions/${response.id}.txt`);\n        resolve();\n      })\n    })\n    .join()\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then( () => {\n      console.log( \"done\" );\n    } )\n    .catch( error => {\n      console.error( error );\n    } );\n};\n\nmain();\n\n```\n\n# Storage\nchats can be stored for later use; this requires a `storage` mechanism. This mechanism manages saving and retrieving chats from a data store. This package includes `LocalStorage` which will save the chats to the local file system and `NoStorage` which does not save any chats. \n\n`LocalStorage` requires an environment variable `CHATS_DIR` where the chats will be stored as `{id}.json` files \n> CHATS_DIR will not be created automatically\n```text\n# .env\nCHATS_DIR=/opt/chats\n```\n\n\n## Custom\nyou can define your own storage mechanism by implementing the `Storage` interface\n\n```javascript\nimport { ChatStorage } from \"@k-apps.io/llm-dsl\";\n\nconst MyAPI: ChatStorage = {\n  getById: ( id: string ): Promise<Chat> => {\n    return new Promise<Chat>( ( resolve, reject ) => {\n      /**\n       * TODO: retrieve an existing chat\n       */\n      reject();\n    } );\n  },\n  save: ( chat: Chat ): Promise<void> => {\n    return new Promise<void>( ( resolve, reject ) => {\n      /**\n       * TODO: save the chat\n       */\n      reject();\n    } );\n  }\n}\n\nconst main = () => {\n  const chat = new DSL({\n    storage: MyAPI,\n    // ...\n  });\n}\n\nmain();\n```\n","readmeFilename":"README.md","gitHead":"acb4fa10d6720b0162bb551298efaf5cfc82eb37","_id":"@k-apps.io/llm-dsl@1.2.1-beta.7","_nodeVersion":"18.13.0","_npmVersion":"9.2.0","dist":{"integrity":"sha512-0iqFZHhbXqj0A0UugK857NXZKR/Nmk1vwR2jOTmAULvQtUPb2eekd/jEodi7eEoEAp6z4dpze/fXIVqJoosVbg==","shasum":"d2d5f60a7d74380d58932b60f4017c3e91b9be02","tarball":"https://registry.npmjs.org/@k-apps.io/llm-dsl/-/llm-dsl-1.2.1-beta.7.tgz","fileCount":14,"unpackedSize":58071,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDu0aNyeV26d1TNmWgDGb/HZMsYS1TIslyEpzmTIu4vDwIhAIWT42rGP64o7rEBeZQmKqo/7SLjaucijUbHDop+G9nx"}]},"_npmUser":{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"},"directories":{},"maintainers":[{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/llm-dsl_1.2.1-beta.7_1704803202928_0.41433615975225213"},"_hasShrinkwrap":false},"1.2.1-beta.8":{"name":"@k-apps.io/llm-dsl","version":"1.2.1-beta.8","description":"a DSL for Large Language Models (LLM)","main":"dist/index.js","scripts":{"test":"jest","build":"tsc","deploy-prod":"npm run build && npm publish --access public","deploy":"npm run build && git add . && git commit -m \"Committing changes before beta deploy\" && npm version prerelease --preid=beta && npm publish --tag beta"},"repository":{"type":"git","url":"git+https://github.com/k-apps-io/llm-dsl.git"},"keywords":["LLM","chatgpt","openai"],"author":{"name":"@imnotakopp"},"license":"ISC","bugs":{"url":"https://github.com/k-apps-io/llm-dsl/issues"},"homepage":"https://github.com/k-apps-io/llm-dsl#readme","devDependencies":{"@k-apps.io/llm-dsl-chatgpt":"^1.2.1-beta.0","@types/jest":"^29.5.1","@types/lodash":"^4.14.200","@types/uuid":"^9.0.6","dotenv":"^16.3.1","jest":"^29.5.0","openai":"^4.14.2","tiktoken":"^1.0.10","ts-jest":"^29.1.0","ts-node":"^10.9.1","typescript":"^5.2.2"},"dependencies":{"ebnf":"^1.9.1","json5":"^2.2.3","lodash":"^4.17.21","uuid":"^9.0.1"},"types":"./dist/index.d.ts","readme":"# todo's\nalign the CommandFunction arguments as a spreadable object\nupdate functions to resolve a string and set the role / visibility to System\nhave a stop call which resolves to then\nhave a condition series like branch\nbranch into sidebars for concurrent processing?\nload a module of functions to add to the chat\n\n# @k-apps.io/llm-dsl\nthis is a DSL for interacting with a LLM, chaining commands together to formulate robust tasks\n\n# Installation\n\n```shell\nnpm install @k-apps.io/llm-dsl\n```\n\npair this with an LLM implementation such as\n\n[@k-apps.io/llm-dsl-chatgpt](https://www.npmjs.com/package/@k-apps.io/llm-dsl-chatgpt)\n\n# Usage\n\n### Hello World\n```javascript\nimport { LLM, DSL, LocalStorage, Rules, CODE_BLOCK_RULE } from \"@k-apps.io/llm-dsl\";\nimport { ChatGPT, Options } from \"@k-apps.io/llm-dsl-chatgpt\";\n\nrequire( \"dotenv\" ).config();\n\nconst main = () => {\n\n  const LLM = new ChatGPT({});\n\n  type Context = string[];\n\n  const chat = new DSL<Options, Context>( {\n    llm: LLM,\n    storage: LocalStorage,\n    options: {\n      model: \"gpt-4\"\n    }\n  } );\n\n  chat\n    .rule( CODE_BLOCK_RULE )\n    .prompt( {\n      message: \"Hello!\"\n    } )\n    .prompt( {\n      message: 'send me 3 progamming languages as a JSON object e.g ```json [\"lang1\", \"lang2\", \"langN\"]```',\n    } )\n    .expect( ( response ) => new Promise<void>( ( resolve, reject ) => {\n      // setting expectations for the response\n      if ( response.codeBlocks === undefined ) {\n        reject( \"expecting a json code block\" );\n      } else if ( response.codeBlocks.length > 1 ) {\n        reject( \"only 1 code block was requested\" );\n      } else {\n        const block = response.codeBlocks[ 0 ];\n        if ( block.lang !== \"json\" ) {\n          reject( `expecting a json code block, found '${ block.lang }' instead` );\n        } else {\n          const data = JSON.parse( block.code );\n          // setting the context in this command makes it available hereafter\n          chat.context = data;\n          resolve();\n        }\n      }\n    } ) )\n    .promptForEach( ( languages: Context ) => {\n      // using this context I can send more prompts\n      return languages.map( language => {\n        const message = `write me a hello world progamming in ${ language }`;\n        return { message: message };\n      } );\n    } )\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then( () => {\n      console.log( \"done\" );\n    } )\n    .catch( error => {\n      console.error( error );\n    } );\n}\n```\n\n# Rules\na `rule` represents a specific instruction for the LLM and `you` to follow. Regardless where you define these in the chain of commands they will always be included at the top of the convesation with `Visibility.REQUIRED`\n\n```javascript\nimport { DSL, Rule } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    .rule({\n      name: \"Bob Uecker\",\n      requirement: \"respond in the style of Bob Uecker, the Famous Radio Broadcaster and not so Fammous American League Baseball Player\"\n    })\n    .prompt({\n      message: \"Uecke, give me a home run call\"\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content );\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Functions\n\nIn your DSL script, you can define custom `functions` that the Language Model (LLM) may call, and the results will be returned as new `Message` objects. This feature allows you to create custom logic and interactions within your conversation with the LLM.\n\n```javascript\nimport { DSL, Rule, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    .function<{ unit?: \"Fahrenheit\" | \"Celsius\"; }>( {\n      name: \"getTheCurrentWeather\",\n      description: \"helps the AI model determine the current weather\",\n      parameters: {\n        \"type\": \"object\",\n        \"properties\": {\n          \"unit\": {\n            \"type\": \"string\",\n            \"enum\": [ \"Fahrenheit\", \"Celsius\" ]\n          }\n        }\n      },\n      func: ( { unit = \"Fahrenheit\", context } ) => {\n        return new Promise( ( resolve, reject ) => {\n          const weather = {\n            temperature: unit === \"Fahrenheit\" ? 24 : 5,\n            unit: unit,\n            percipitation: \"it's snowing\"\n          };\n          resolve( {\n            message: `here is the current weather report \\`\\`\\`json ${ JSON.stringify( weather ) }\\`\\`\\``\n          } );\n        } );\n      }\n    } )\n    .rule( {\n      name: \"Temperature\",\n      requirement: \"all temperature readings must be in Celsius\"\n    } )\n    .prompt( {\n      message: \"what's the weather like today?\"\n    } )\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content );\n    } )\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Prompting\na `prompt` is the main form of interacting with the LLM, use this command to send a message and generate a response\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL<Options, string>({});\n  chat.context = \"what is something the bank has that could be used on the football field?\";\n  chat\n    .prompt({\n      message: \"Why did the football team go to the bank?\"\n    })\n    .prompt( context => {\n      // a prompt can also access the context of the chat via a `CommandFunction`\n      return {\n        message: `here is a hint: ${context}`\n      }\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Context\n\nthe `context` is a shared space that enables the seamless exchange of information between different commands and prompts. It's a vital tool for tasks that involve gathering, adjusting, or retrieving data across multiple interactions. The `context` ensures your conversation flows cohesively and empowers you to manage and transfer artifacts effortlessly.\n\nTo manage the `context`, you can use commands like [response](#response) and [expect](#expect) or call `setContext` directly\n\nTo use the `context`, checkout [prompt](#prompting), [promptForEach](#promptforeach) and [branchForEach](#branchforeach)\n\n\n## response\n`response` provides direct accesss the response of a prompt, it gives you control to continue the chain of commands by calling `resolve` or stop by calling `reject`\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL<Options, string>({});\n  chat\n    .prompt({\n      message: \"finish the sentence 'one does not'\"\n    })\n    .response( response => new Promise<void>(( resolve, reject ) => {\n      if ( response.content.includes(\"Mordor\")) {\n        chat.context = {\n          isChill: true,\n        }\n      } else {\n        chat.context = {\n          isChill: false,\n          tasks: [ \"watch LOTR\" ]\n        }\n      }\n      resolve();\n    }))\n    .prompt( ( context, $this ) => {\n      if ( context.isChill ) {\n        return {\n          message: \"great answer\"\n        }\n      } else {\n        return {\n          message: \"I need you to watch LOTR\"\n        }\n      }\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n## expect\nThe `expect` function is another way to access the response of a prompt and set expectations for it. When working with the Language Model (LLM), you can use this command to define specific criteria that the response should meet. If these criteria are not met, you have the option to call `reject(\"with your reason\")` to initiate a [dispute resolution](#dispute-resolution) process. On the other hand, if the response aligns with your expectations, you can use the `resolve()` method to continue the chain of interactions.\n\nThis command plays a pivotal role in ensuring that the Language Model's responses are in line with your desired outcomes. It serves as a valuable tool for quality control and enables the creation of custom interactions with the model. By setting clear expectations and handling responses accordingly, you can shape the behavior of the model to meet your specific needs.\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\ninterface Club {\n  club: string;\n  yardage: number;\n}\n\nconst main = () => {\n  const chat = new DSL<Options, Club[]>({});\n  chat\n    .prompt({\n      message: \"please generate a JSON Array of golf clubs I could hit out of the ruff. This JSON array must structured as ```javascript {\\noptions: { club: string; yardage: number; }[]\\n}``` and be provided as a JSON code block\"\n    })\n    .expect( response => new Promise<void>(( resolve, reject ) => {\n      // I have high expecations\n      if ( response.codeBlocks === undefined ) {\n        reject(\"a json code block was expected\")\n      } else if ( response.codeBlocks.length > 1 ) {\n        reject(\"too many code blocks, only 1 is required\")\n      } else {\n        const block = response.codeBlocks[0];\n        if ( block.lang !== \"json\" ) {\n          reject(\"code block must be 'json'\")\n        } else {\n          const data = JSON.parse(block.code);\n          chat.context = data.options;\n          resolve();\n        }\n      }\n    }))\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n#### Dispute Resolution\n\nWhen you call `reject(\"with your reason\")`, the DSL will initiate a `sidebar` chat to resolve the dispute between your expectations and the prior response. This `sidebar` chat retains the `conetxt`, `rules` and `functions` you originally defined in the main chat.\n\nEach response generated in the `sidebar` will be assessed against your expectations until they are met or the maximum call stack limit is reached. \n\n> Adjust the expect max call stack with `setMaxCallStack`\n\nThe response that aligns with your expectations will be marked with `Visibility.OPTIONAL` and will follow the response that triggered the dispute. The initial response that led to the dispute will be updated with a visibility of `Visibility.HIDDEN`, and the chain of commands will resume.\n\n## promptForEach\nThe `promptForEach` command allows you to use the chat `context` to generate one or more prompts, each of which is executed sequentially and synchronously. This function is particularly useful when you need to create multiple prompts dynamically based on the context data.\n\n\n```javascript\nimport { DSL } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    /** continued from above */\n    .promptForEach( ( context: Club[] ) => {\n      return context.map(({ club, yardage}, index ) => {\n        return {\n          message: `I expect the ruff to take about 40% off the distance of the club, what is the new yardarge for ${club}?`\n        }\n      })\n    })\n    .prompt({\n      message: \"I have 120y to the hole and I want to carry it 115y, which club should I use?\"\n    })\n    .prompt({\n      message: \"would this be a full swing?\"\n    })\n    .prompt({\n      message: \"I stuck it close, let's get some birds\",\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n## branchForEach\n`branchForEach` similary enables you to use the chat `context` to generate a series of commands. The series is scoped to each command between the `branchForEach` and the next `join` command. Each series is executed sequentially and synchronously.\n\n```javascript\nimport { DSL } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const LLM = new ChatGPT( {} );\n\n  const chat = new DSL<PromptOptions, Context>( {\n    llm: LLM,\n    storage: LocalStorage,\n    options: {\n      model: \"gpt-4\",\n      temperature: 0.5,\n      top_p: 0.5\n    }\n  } );\n\n  chat\n    .rule( CODE_BLOCK_RULE )\n    .prompt( {\n      message: \"generate a list of cities implementing the ```typescript interface { cities: string[] }``` as a json code block\"\n    } )\n    .expect( response => {\n      // todo - set the expectations\n      const data = JSON.parse( response.codeBlocks![0].code );\n      chat.context = data.cities;\n      resolve();\n    })\n    .branchForEach( context => {\n      return context.map( city => {\n        return {\n          message: `consider the following city, ${ city } and pick a monument, artificat or famous person in this city we'll call this the target`\n        };\n      } );\n    } )\n    .prompt( {\n      message: \"consider the RPG table top game Sprawl, create a new mission in this city that involves our target\"\n    } )\n    .response( response => {\n      // write the response to a file\n      return new Promise<void>(( resolve, reject ) => {\n        writeFileSync(`./sprawl-missions/${response.id}.txt`);\n        resolve();\n      })\n    })\n    .join()\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then( () => {\n      console.log( \"done\" );\n    } )\n    .catch( error => {\n      console.error( error );\n    } );\n};\n\nmain();\n\n```\n\n# Storage\nchats can be stored for later use; this requires a `storage` mechanism. This mechanism manages saving and retrieving chats from a data store. This package includes `LocalStorage` which will save the chats to the local file system and `NoStorage` which does not save any chats. \n\n`LocalStorage` requires an environment variable `CHATS_DIR` where the chats will be stored as `{id}.json` files \n> CHATS_DIR will not be created automatically\n```text\n# .env\nCHATS_DIR=/opt/chats\n```\n\n\n## Custom\nyou can define your own storage mechanism by implementing the `Storage` interface\n\n```javascript\nimport { ChatStorage } from \"@k-apps.io/llm-dsl\";\n\nconst MyAPI: ChatStorage = {\n  getById: ( id: string ): Promise<Chat> => {\n    return new Promise<Chat>( ( resolve, reject ) => {\n      /**\n       * TODO: retrieve an existing chat\n       */\n      reject();\n    } );\n  },\n  save: ( chat: Chat ): Promise<void> => {\n    return new Promise<void>( ( resolve, reject ) => {\n      /**\n       * TODO: save the chat\n       */\n      reject();\n    } );\n  }\n}\n\nconst main = () => {\n  const chat = new DSL({\n    storage: MyAPI,\n    // ...\n  });\n}\n\nmain();\n```\n","readmeFilename":"README.md","gitHead":"25ba22a8eb7d3c8e9966d1fb65fc88d800d67f16","_id":"@k-apps.io/llm-dsl@1.2.1-beta.8","_nodeVersion":"18.13.0","_npmVersion":"9.2.0","dist":{"integrity":"sha512-D61ttAs2QAHBTZbQ++hLNxbXtGE03SweSQ4MGvKSO/4gMSNyhfpa2yTf8vPKQeJPLj9SYjIuiLm7CeSbq0s9Bg==","shasum":"244baafa847b19156fa95bfebba51430641bc4db","tarball":"https://registry.npmjs.org/@k-apps.io/llm-dsl/-/llm-dsl-1.2.1-beta.8.tgz","fileCount":16,"unpackedSize":60204,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHjptiIoR04tjCl/oNoXmG8sJuETMhEp/aZPobotPSDkAiEA10oMUWo28wnE7/gSAgTbr8o482R+ZwGh7nwLc59NVJw="}]},"_npmUser":{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"},"directories":{},"maintainers":[{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/llm-dsl_1.2.1-beta.8_1704807228493_0.4171484785451298"},"_hasShrinkwrap":false},"1.2.1-beta.9":{"name":"@k-apps.io/llm-dsl","version":"1.2.1-beta.9","description":"a DSL for Large Language Models (LLM)","main":"dist/index.js","scripts":{"test":"jest","build":"tsc","deploy-prod":"npm run build && npm publish --access public","deploy":"npm run build && git add . && git commit -m \"Committing changes before beta deploy\" && npm version prerelease --preid=beta && npm publish --tag beta"},"repository":{"type":"git","url":"git+https://github.com/k-apps-io/llm-dsl.git"},"keywords":["LLM","chatgpt","openai"],"author":{"name":"@imnotakopp"},"license":"ISC","bugs":{"url":"https://github.com/k-apps-io/llm-dsl/issues"},"homepage":"https://github.com/k-apps-io/llm-dsl#readme","devDependencies":{"@k-apps.io/llm-dsl-chatgpt":"^1.2.1-beta.0","@types/jest":"^29.5.1","@types/lodash":"^4.14.200","@types/uuid":"^9.0.6","dotenv":"^16.3.1","jest":"^29.5.0","openai":"^4.14.2","tiktoken":"^1.0.10","ts-jest":"^29.1.0","ts-node":"^10.9.1","typescript":"^5.2.2"},"dependencies":{"ebnf":"^1.9.1","json5":"^2.2.3","lodash":"^4.17.21","uuid":"^9.0.1"},"types":"./dist/index.d.ts","readme":"# todo's\nalign the CommandFunction arguments as a spreadable object\nupdate functions to resolve a string and set the role / visibility to System\nhave a stop call which resolves to then\nhave a condition series like branch\nbranch into sidebars for concurrent processing?\nload a module of functions to add to the chat\n\n# @k-apps.io/llm-dsl\nthis is a DSL for interacting with a LLM, chaining commands together to formulate robust tasks\n\n# Installation\n\n```shell\nnpm install @k-apps.io/llm-dsl\n```\n\npair this with an LLM implementation such as\n\n[@k-apps.io/llm-dsl-chatgpt](https://www.npmjs.com/package/@k-apps.io/llm-dsl-chatgpt)\n\n# Usage\n\n### Hello World\n```javascript\nimport { LLM, DSL, LocalStorage, Rules, CODE_BLOCK_RULE } from \"@k-apps.io/llm-dsl\";\nimport { ChatGPT, Options } from \"@k-apps.io/llm-dsl-chatgpt\";\n\nrequire( \"dotenv\" ).config();\n\nconst main = () => {\n\n  const LLM = new ChatGPT({});\n\n  type Context = string[];\n\n  const chat = new DSL<Options, Context>( {\n    llm: LLM,\n    storage: LocalStorage,\n    options: {\n      model: \"gpt-4\"\n    }\n  } );\n\n  chat\n    .rule( CODE_BLOCK_RULE )\n    .prompt( {\n      message: \"Hello!\"\n    } )\n    .prompt( {\n      message: 'send me 3 progamming languages as a JSON object e.g ```json [\"lang1\", \"lang2\", \"langN\"]```',\n    } )\n    .expect( ( response ) => new Promise<void>( ( resolve, reject ) => {\n      // setting expectations for the response\n      if ( response.codeBlocks === undefined ) {\n        reject( \"expecting a json code block\" );\n      } else if ( response.codeBlocks.length > 1 ) {\n        reject( \"only 1 code block was requested\" );\n      } else {\n        const block = response.codeBlocks[ 0 ];\n        if ( block.lang !== \"json\" ) {\n          reject( `expecting a json code block, found '${ block.lang }' instead` );\n        } else {\n          const data = JSON.parse( block.code );\n          // setting the context in this command makes it available hereafter\n          chat.context = data;\n          resolve();\n        }\n      }\n    } ) )\n    .promptForEach( ( languages: Context ) => {\n      // using this context I can send more prompts\n      return languages.map( language => {\n        const message = `write me a hello world progamming in ${ language }`;\n        return { message: message };\n      } );\n    } )\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then( () => {\n      console.log( \"done\" );\n    } )\n    .catch( error => {\n      console.error( error );\n    } );\n}\n```\n\n# Rules\na `rule` represents a specific instruction for the LLM and `you` to follow. Regardless where you define these in the chain of commands they will always be included at the top of the convesation with `Visibility.REQUIRED`\n\n```javascript\nimport { DSL, Rule } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    .rule({\n      name: \"Bob Uecker\",\n      requirement: \"respond in the style of Bob Uecker, the Famous Radio Broadcaster and not so Fammous American League Baseball Player\"\n    })\n    .prompt({\n      message: \"Uecke, give me a home run call\"\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content );\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Functions\n\nIn your DSL script, you can define custom `functions` that the Language Model (LLM) may call, and the results will be returned as new `Message` objects. This feature allows you to create custom logic and interactions within your conversation with the LLM.\n\n```javascript\nimport { DSL, Rule, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    .function<{ unit?: \"Fahrenheit\" | \"Celsius\"; }>( {\n      name: \"getTheCurrentWeather\",\n      description: \"helps the AI model determine the current weather\",\n      parameters: {\n        \"type\": \"object\",\n        \"properties\": {\n          \"unit\": {\n            \"type\": \"string\",\n            \"enum\": [ \"Fahrenheit\", \"Celsius\" ]\n          }\n        }\n      },\n      func: ( { unit = \"Fahrenheit\", context } ) => {\n        return new Promise( ( resolve, reject ) => {\n          const weather = {\n            temperature: unit === \"Fahrenheit\" ? 24 : 5,\n            unit: unit,\n            percipitation: \"it's snowing\"\n          };\n          resolve( {\n            message: `here is the current weather report \\`\\`\\`json ${ JSON.stringify( weather ) }\\`\\`\\``\n          } );\n        } );\n      }\n    } )\n    .rule( {\n      name: \"Temperature\",\n      requirement: \"all temperature readings must be in Celsius\"\n    } )\n    .prompt( {\n      message: \"what's the weather like today?\"\n    } )\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content );\n    } )\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Prompting\na `prompt` is the main form of interacting with the LLM, use this command to send a message and generate a response\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL<Options, string>({});\n  chat.context = \"what is something the bank has that could be used on the football field?\";\n  chat\n    .prompt({\n      message: \"Why did the football team go to the bank?\"\n    })\n    .prompt( context => {\n      // a prompt can also access the context of the chat via a `CommandFunction`\n      return {\n        message: `here is a hint: ${context}`\n      }\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Context\n\nthe `context` is a shared space that enables the seamless exchange of information between different commands and prompts. It's a vital tool for tasks that involve gathering, adjusting, or retrieving data across multiple interactions. The `context` ensures your conversation flows cohesively and empowers you to manage and transfer artifacts effortlessly.\n\nTo manage the `context`, you can use commands like [response](#response) and [expect](#expect) or call `setContext` directly\n\nTo use the `context`, checkout [prompt](#prompting), [promptForEach](#promptforeach) and [branchForEach](#branchforeach)\n\n\n## response\n`response` provides direct accesss the response of a prompt, it gives you control to continue the chain of commands by calling `resolve` or stop by calling `reject`\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL<Options, string>({});\n  chat\n    .prompt({\n      message: \"finish the sentence 'one does not'\"\n    })\n    .response( response => new Promise<void>(( resolve, reject ) => {\n      if ( response.content.includes(\"Mordor\")) {\n        chat.context = {\n          isChill: true,\n        }\n      } else {\n        chat.context = {\n          isChill: false,\n          tasks: [ \"watch LOTR\" ]\n        }\n      }\n      resolve();\n    }))\n    .prompt( ( context, $this ) => {\n      if ( context.isChill ) {\n        return {\n          message: \"great answer\"\n        }\n      } else {\n        return {\n          message: \"I need you to watch LOTR\"\n        }\n      }\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n## expect\nThe `expect` function is another way to access the response of a prompt and set expectations for it. When working with the Language Model (LLM), you can use this command to define specific criteria that the response should meet. If these criteria are not met, you have the option to call `reject(\"with your reason\")` to initiate a [dispute resolution](#dispute-resolution) process. On the other hand, if the response aligns with your expectations, you can use the `resolve()` method to continue the chain of interactions.\n\nThis command plays a pivotal role in ensuring that the Language Model's responses are in line with your desired outcomes. It serves as a valuable tool for quality control and enables the creation of custom interactions with the model. By setting clear expectations and handling responses accordingly, you can shape the behavior of the model to meet your specific needs.\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\ninterface Club {\n  club: string;\n  yardage: number;\n}\n\nconst main = () => {\n  const chat = new DSL<Options, Club[]>({});\n  chat\n    .prompt({\n      message: \"please generate a JSON Array of golf clubs I could hit out of the ruff. This JSON array must structured as ```javascript {\\noptions: { club: string; yardage: number; }[]\\n}``` and be provided as a JSON code block\"\n    })\n    .expect( response => new Promise<void>(( resolve, reject ) => {\n      // I have high expecations\n      if ( response.codeBlocks === undefined ) {\n        reject(\"a json code block was expected\")\n      } else if ( response.codeBlocks.length > 1 ) {\n        reject(\"too many code blocks, only 1 is required\")\n      } else {\n        const block = response.codeBlocks[0];\n        if ( block.lang !== \"json\" ) {\n          reject(\"code block must be 'json'\")\n        } else {\n          const data = JSON.parse(block.code);\n          chat.context = data.options;\n          resolve();\n        }\n      }\n    }))\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n#### Dispute Resolution\n\nWhen you call `reject(\"with your reason\")`, the DSL will initiate a `sidebar` chat to resolve the dispute between your expectations and the prior response. This `sidebar` chat retains the `conetxt`, `rules` and `functions` you originally defined in the main chat.\n\nEach response generated in the `sidebar` will be assessed against your expectations until they are met or the maximum call stack limit is reached. \n\n> Adjust the expect max call stack with `setMaxCallStack`\n\nThe response that aligns with your expectations will be marked with `Visibility.OPTIONAL` and will follow the response that triggered the dispute. The initial response that led to the dispute will be updated with a visibility of `Visibility.HIDDEN`, and the chain of commands will resume.\n\n## promptForEach\nThe `promptForEach` command allows you to use the chat `context` to generate one or more prompts, each of which is executed sequentially and synchronously. This function is particularly useful when you need to create multiple prompts dynamically based on the context data.\n\n\n```javascript\nimport { DSL } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    /** continued from above */\n    .promptForEach( ( context: Club[] ) => {\n      return context.map(({ club, yardage}, index ) => {\n        return {\n          message: `I expect the ruff to take about 40% off the distance of the club, what is the new yardarge for ${club}?`\n        }\n      })\n    })\n    .prompt({\n      message: \"I have 120y to the hole and I want to carry it 115y, which club should I use?\"\n    })\n    .prompt({\n      message: \"would this be a full swing?\"\n    })\n    .prompt({\n      message: \"I stuck it close, let's get some birds\",\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n## branchForEach\n`branchForEach` similary enables you to use the chat `context` to generate a series of commands. The series is scoped to each command between the `branchForEach` and the next `join` command. Each series is executed sequentially and synchronously.\n\n```javascript\nimport { DSL } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const LLM = new ChatGPT( {} );\n\n  const chat = new DSL<PromptOptions, Context>( {\n    llm: LLM,\n    storage: LocalStorage,\n    options: {\n      model: \"gpt-4\",\n      temperature: 0.5,\n      top_p: 0.5\n    }\n  } );\n\n  chat\n    .rule( CODE_BLOCK_RULE )\n    .prompt( {\n      message: \"generate a list of cities implementing the ```typescript interface { cities: string[] }``` as a json code block\"\n    } )\n    .expect( response => {\n      // todo - set the expectations\n      const data = JSON.parse( response.codeBlocks![0].code );\n      chat.context = data.cities;\n      resolve();\n    })\n    .branchForEach( context => {\n      return context.map( city => {\n        return {\n          message: `consider the following city, ${ city } and pick a monument, artificat or famous person in this city we'll call this the target`\n        };\n      } );\n    } )\n    .prompt( {\n      message: \"consider the RPG table top game Sprawl, create a new mission in this city that involves our target\"\n    } )\n    .response( response => {\n      // write the response to a file\n      return new Promise<void>(( resolve, reject ) => {\n        writeFileSync(`./sprawl-missions/${response.id}.txt`);\n        resolve();\n      })\n    })\n    .join()\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then( () => {\n      console.log( \"done\" );\n    } )\n    .catch( error => {\n      console.error( error );\n    } );\n};\n\nmain();\n\n```\n\n# Storage\nchats can be stored for later use; this requires a `storage` mechanism. This mechanism manages saving and retrieving chats from a data store. This package includes `LocalStorage` which will save the chats to the local file system and `NoStorage` which does not save any chats. \n\n`LocalStorage` requires an environment variable `CHATS_DIR` where the chats will be stored as `{id}.json` files \n> CHATS_DIR will not be created automatically\n```text\n# .env\nCHATS_DIR=/opt/chats\n```\n\n\n## Custom\nyou can define your own storage mechanism by implementing the `Storage` interface\n\n```javascript\nimport { ChatStorage } from \"@k-apps.io/llm-dsl\";\n\nconst MyAPI: ChatStorage = {\n  getById: ( id: string ): Promise<Chat> => {\n    return new Promise<Chat>( ( resolve, reject ) => {\n      /**\n       * TODO: retrieve an existing chat\n       */\n      reject();\n    } );\n  },\n  save: ( chat: Chat ): Promise<void> => {\n    return new Promise<void>( ( resolve, reject ) => {\n      /**\n       * TODO: save the chat\n       */\n      reject();\n    } );\n  }\n}\n\nconst main = () => {\n  const chat = new DSL({\n    storage: MyAPI,\n    // ...\n  });\n}\n\nmain();\n```\n","readmeFilename":"README.md","gitHead":"7f1e073d456894a466e4d72a30a41f1b4460feb7","_id":"@k-apps.io/llm-dsl@1.2.1-beta.9","_nodeVersion":"18.13.0","_npmVersion":"9.2.0","dist":{"integrity":"sha512-RZjGEYAtAaA1TWas/4V9UnPpGNOgAeoZWt2zcqDTXzVUdiAYF1389ICkw0wKFRxGP/eeqFjLEAVvN8GuX9F/Hg==","shasum":"f115e1c21e38aab175d937d50dba65d5d0a94b93","tarball":"https://registry.npmjs.org/@k-apps.io/llm-dsl/-/llm-dsl-1.2.1-beta.9.tgz","fileCount":16,"unpackedSize":60398,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDwRtiNeTTPKiYR8HnJJQQ++146EqekN0NxqHi+O5w7rAIhANbno6V1UtOS26YaDtQaJQQKj7cdnIH6h2rer/zm4NGD"}]},"_npmUser":{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"},"directories":{},"maintainers":[{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/llm-dsl_1.2.1-beta.9_1704807299643_0.17945510708589407"},"_hasShrinkwrap":false},"1.2.1-beta.10":{"name":"@k-apps.io/llm-dsl","version":"1.2.1-beta.10","description":"a DSL for Large Language Models (LLM)","main":"dist/index.js","scripts":{"test":"jest","build":"tsc","deploy-prod":"npm run build && npm publish --access public","deploy":"npm run build && git add . && git commit -m \"Committing changes before beta deploy\" && npm version prerelease --preid=beta && npm publish --tag beta"},"repository":{"type":"git","url":"git+https://github.com/k-apps-io/llm-dsl.git"},"keywords":["LLM","chatgpt","openai"],"author":{"name":"@imnotakopp"},"license":"ISC","bugs":{"url":"https://github.com/k-apps-io/llm-dsl/issues"},"homepage":"https://github.com/k-apps-io/llm-dsl#readme","devDependencies":{"@k-apps.io/llm-dsl-chatgpt":"^1.2.1-beta.0","@types/jest":"^29.5.1","@types/lodash":"^4.14.200","@types/uuid":"^9.0.6","dotenv":"^16.3.1","jest":"^29.5.0","openai":"^4.14.2","tiktoken":"^1.0.10","ts-jest":"^29.1.0","ts-node":"^10.9.1","typescript":"^5.2.2"},"dependencies":{"ebnf":"^1.9.1","json5":"^2.2.3","lodash":"^4.17.21","uuid":"^9.0.1"},"types":"./dist/index.d.ts","readme":"# todo's\nalign the CommandFunction arguments as a spreadable object\nupdate functions to resolve a string and set the role / visibility to System\nhave a stop call which resolves to then\nhave a condition series like branch\nbranch into sidebars for concurrent processing?\nload a module of functions to add to the chat\n\n# @k-apps.io/llm-dsl\nthis is a DSL for interacting with a LLM, chaining commands together to formulate robust tasks\n\n# Installation\n\n```shell\nnpm install @k-apps.io/llm-dsl\n```\n\npair this with an LLM implementation such as\n\n[@k-apps.io/llm-dsl-chatgpt](https://www.npmjs.com/package/@k-apps.io/llm-dsl-chatgpt)\n\n# Usage\n\n### Hello World\n```javascript\nimport { LLM, DSL, LocalStorage, Rules, CODE_BLOCK_RULE } from \"@k-apps.io/llm-dsl\";\nimport { ChatGPT, Options } from \"@k-apps.io/llm-dsl-chatgpt\";\n\nrequire( \"dotenv\" ).config();\n\nconst main = () => {\n\n  const LLM = new ChatGPT({});\n\n  type Context = string[];\n\n  const chat = new DSL<Options, Context>( {\n    llm: LLM,\n    storage: LocalStorage,\n    options: {\n      model: \"gpt-4\"\n    }\n  } );\n\n  chat\n    .rule( CODE_BLOCK_RULE )\n    .prompt( {\n      message: \"Hello!\"\n    } )\n    .prompt( {\n      message: 'send me 3 progamming languages as a JSON object e.g ```json [\"lang1\", \"lang2\", \"langN\"]```',\n    } )\n    .expect( ( response ) => new Promise<void>( ( resolve, reject ) => {\n      // setting expectations for the response\n      if ( response.codeBlocks === undefined ) {\n        reject( \"expecting a json code block\" );\n      } else if ( response.codeBlocks.length > 1 ) {\n        reject( \"only 1 code block was requested\" );\n      } else {\n        const block = response.codeBlocks[ 0 ];\n        if ( block.lang !== \"json\" ) {\n          reject( `expecting a json code block, found '${ block.lang }' instead` );\n        } else {\n          const data = JSON.parse( block.code );\n          // setting the context in this command makes it available hereafter\n          chat.context = data;\n          resolve();\n        }\n      }\n    } ) )\n    .promptForEach( ( languages: Context ) => {\n      // using this context I can send more prompts\n      return languages.map( language => {\n        const message = `write me a hello world progamming in ${ language }`;\n        return { message: message };\n      } );\n    } )\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then( () => {\n      console.log( \"done\" );\n    } )\n    .catch( error => {\n      console.error( error );\n    } );\n}\n```\n\n# Rules\na `rule` represents a specific instruction for the LLM and `you` to follow. Regardless where you define these in the chain of commands they will always be included at the top of the convesation with `Visibility.REQUIRED`\n\n```javascript\nimport { DSL, Rule } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    .rule({\n      name: \"Bob Uecker\",\n      requirement: \"respond in the style of Bob Uecker, the Famous Radio Broadcaster and not so Fammous American League Baseball Player\"\n    })\n    .prompt({\n      message: \"Uecke, give me a home run call\"\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content );\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Functions\n\nIn your DSL script, you can define custom `functions` that the Language Model (LLM) may call, and the results will be returned as new `Message` objects. This feature allows you to create custom logic and interactions within your conversation with the LLM.\n\n```javascript\nimport { DSL, Rule, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    .function<{ unit?: \"Fahrenheit\" | \"Celsius\"; }>( {\n      name: \"getTheCurrentWeather\",\n      description: \"helps the AI model determine the current weather\",\n      parameters: {\n        \"type\": \"object\",\n        \"properties\": {\n          \"unit\": {\n            \"type\": \"string\",\n            \"enum\": [ \"Fahrenheit\", \"Celsius\" ]\n          }\n        }\n      },\n      func: ( { unit = \"Fahrenheit\", context } ) => {\n        return new Promise( ( resolve, reject ) => {\n          const weather = {\n            temperature: unit === \"Fahrenheit\" ? 24 : 5,\n            unit: unit,\n            percipitation: \"it's snowing\"\n          };\n          resolve( {\n            message: `here is the current weather report \\`\\`\\`json ${ JSON.stringify( weather ) }\\`\\`\\``\n          } );\n        } );\n      }\n    } )\n    .rule( {\n      name: \"Temperature\",\n      requirement: \"all temperature readings must be in Celsius\"\n    } )\n    .prompt( {\n      message: \"what's the weather like today?\"\n    } )\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content );\n    } )\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Prompting\na `prompt` is the main form of interacting with the LLM, use this command to send a message and generate a response\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL<Options, string>({});\n  chat.context = \"what is something the bank has that could be used on the football field?\";\n  chat\n    .prompt({\n      message: \"Why did the football team go to the bank?\"\n    })\n    .prompt( context => {\n      // a prompt can also access the context of the chat via a `CommandFunction`\n      return {\n        message: `here is a hint: ${context}`\n      }\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Context\n\nthe `context` is a shared space that enables the seamless exchange of information between different commands and prompts. It's a vital tool for tasks that involve gathering, adjusting, or retrieving data across multiple interactions. The `context` ensures your conversation flows cohesively and empowers you to manage and transfer artifacts effortlessly.\n\nTo manage the `context`, you can use commands like [response](#response) and [expect](#expect) or call `setContext` directly\n\nTo use the `context`, checkout [prompt](#prompting), [promptForEach](#promptforeach) and [branchForEach](#branchforeach)\n\n\n## response\n`response` provides direct accesss the response of a prompt, it gives you control to continue the chain of commands by calling `resolve` or stop by calling `reject`\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL<Options, string>({});\n  chat\n    .prompt({\n      message: \"finish the sentence 'one does not'\"\n    })\n    .response( response => new Promise<void>(( resolve, reject ) => {\n      if ( response.content.includes(\"Mordor\")) {\n        chat.context = {\n          isChill: true,\n        }\n      } else {\n        chat.context = {\n          isChill: false,\n          tasks: [ \"watch LOTR\" ]\n        }\n      }\n      resolve();\n    }))\n    .prompt( ( context, $this ) => {\n      if ( context.isChill ) {\n        return {\n          message: \"great answer\"\n        }\n      } else {\n        return {\n          message: \"I need you to watch LOTR\"\n        }\n      }\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n## expect\nThe `expect` function is another way to access the response of a prompt and set expectations for it. When working with the Language Model (LLM), you can use this command to define specific criteria that the response should meet. If these criteria are not met, you have the option to call `reject(\"with your reason\")` to initiate a [dispute resolution](#dispute-resolution) process. On the other hand, if the response aligns with your expectations, you can use the `resolve()` method to continue the chain of interactions.\n\nThis command plays a pivotal role in ensuring that the Language Model's responses are in line with your desired outcomes. It serves as a valuable tool for quality control and enables the creation of custom interactions with the model. By setting clear expectations and handling responses accordingly, you can shape the behavior of the model to meet your specific needs.\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\ninterface Club {\n  club: string;\n  yardage: number;\n}\n\nconst main = () => {\n  const chat = new DSL<Options, Club[]>({});\n  chat\n    .prompt({\n      message: \"please generate a JSON Array of golf clubs I could hit out of the ruff. This JSON array must structured as ```javascript {\\noptions: { club: string; yardage: number; }[]\\n}``` and be provided as a JSON code block\"\n    })\n    .expect( response => new Promise<void>(( resolve, reject ) => {\n      // I have high expecations\n      if ( response.codeBlocks === undefined ) {\n        reject(\"a json code block was expected\")\n      } else if ( response.codeBlocks.length > 1 ) {\n        reject(\"too many code blocks, only 1 is required\")\n      } else {\n        const block = response.codeBlocks[0];\n        if ( block.lang !== \"json\" ) {\n          reject(\"code block must be 'json'\")\n        } else {\n          const data = JSON.parse(block.code);\n          chat.context = data.options;\n          resolve();\n        }\n      }\n    }))\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n#### Dispute Resolution\n\nWhen you call `reject(\"with your reason\")`, the DSL will initiate a `sidebar` chat to resolve the dispute between your expectations and the prior response. This `sidebar` chat retains the `conetxt`, `rules` and `functions` you originally defined in the main chat.\n\nEach response generated in the `sidebar` will be assessed against your expectations until they are met or the maximum call stack limit is reached. \n\n> Adjust the expect max call stack with `setMaxCallStack`\n\nThe response that aligns with your expectations will be marked with `Visibility.OPTIONAL` and will follow the response that triggered the dispute. The initial response that led to the dispute will be updated with a visibility of `Visibility.HIDDEN`, and the chain of commands will resume.\n\n## promptForEach\nThe `promptForEach` command allows you to use the chat `context` to generate one or more prompts, each of which is executed sequentially and synchronously. This function is particularly useful when you need to create multiple prompts dynamically based on the context data.\n\n\n```javascript\nimport { DSL } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    /** continued from above */\n    .promptForEach( ( context: Club[] ) => {\n      return context.map(({ club, yardage}, index ) => {\n        return {\n          message: `I expect the ruff to take about 40% off the distance of the club, what is the new yardarge for ${club}?`\n        }\n      })\n    })\n    .prompt({\n      message: \"I have 120y to the hole and I want to carry it 115y, which club should I use?\"\n    })\n    .prompt({\n      message: \"would this be a full swing?\"\n    })\n    .prompt({\n      message: \"I stuck it close, let's get some birds\",\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n## branchForEach\n`branchForEach` similary enables you to use the chat `context` to generate a series of commands. The series is scoped to each command between the `branchForEach` and the next `join` command. Each series is executed sequentially and synchronously.\n\n```javascript\nimport { DSL } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const LLM = new ChatGPT( {} );\n\n  const chat = new DSL<PromptOptions, Context>( {\n    llm: LLM,\n    storage: LocalStorage,\n    options: {\n      model: \"gpt-4\",\n      temperature: 0.5,\n      top_p: 0.5\n    }\n  } );\n\n  chat\n    .rule( CODE_BLOCK_RULE )\n    .prompt( {\n      message: \"generate a list of cities implementing the ```typescript interface { cities: string[] }``` as a json code block\"\n    } )\n    .expect( response => {\n      // todo - set the expectations\n      const data = JSON.parse( response.codeBlocks![0].code );\n      chat.context = data.cities;\n      resolve();\n    })\n    .branchForEach( context => {\n      return context.map( city => {\n        return {\n          message: `consider the following city, ${ city } and pick a monument, artificat or famous person in this city we'll call this the target`\n        };\n      } );\n    } )\n    .prompt( {\n      message: \"consider the RPG table top game Sprawl, create a new mission in this city that involves our target\"\n    } )\n    .response( response => {\n      // write the response to a file\n      return new Promise<void>(( resolve, reject ) => {\n        writeFileSync(`./sprawl-missions/${response.id}.txt`);\n        resolve();\n      })\n    })\n    .join()\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then( () => {\n      console.log( \"done\" );\n    } )\n    .catch( error => {\n      console.error( error );\n    } );\n};\n\nmain();\n\n```\n\n# Storage\nchats can be stored for later use; this requires a `storage` mechanism. This mechanism manages saving and retrieving chats from a data store. This package includes `LocalStorage` which will save the chats to the local file system and `NoStorage` which does not save any chats. \n\n`LocalStorage` requires an environment variable `CHATS_DIR` where the chats will be stored as `{id}.json` files \n> CHATS_DIR will not be created automatically\n```text\n# .env\nCHATS_DIR=/opt/chats\n```\n\n\n## Custom\nyou can define your own storage mechanism by implementing the `Storage` interface\n\n```javascript\nimport { ChatStorage } from \"@k-apps.io/llm-dsl\";\n\nconst MyAPI: ChatStorage = {\n  getById: ( id: string ): Promise<Chat> => {\n    return new Promise<Chat>( ( resolve, reject ) => {\n      /**\n       * TODO: retrieve an existing chat\n       */\n      reject();\n    } );\n  },\n  save: ( chat: Chat ): Promise<void> => {\n    return new Promise<void>( ( resolve, reject ) => {\n      /**\n       * TODO: save the chat\n       */\n      reject();\n    } );\n  }\n}\n\nconst main = () => {\n  const chat = new DSL({\n    storage: MyAPI,\n    // ...\n  });\n}\n\nmain();\n```\n","readmeFilename":"README.md","gitHead":"23f14cbfee97d7ee3d6e2b3cbfd0e244468bfc98","_id":"@k-apps.io/llm-dsl@1.2.1-beta.10","_nodeVersion":"18.13.0","_npmVersion":"9.2.0","dist":{"integrity":"sha512-EvTn/PSJIv0CrILxPVyxT050iorDY96ffjZ+A9Wb/0vzkaEA+/QICBU/FiILqnAqtFaQ9GI/wo66+T2gZTflLQ==","shasum":"cc1665a0895143977ad90aa252a7bcc932cb4f9e","tarball":"https://registry.npmjs.org/@k-apps.io/llm-dsl/-/llm-dsl-1.2.1-beta.10.tgz","fileCount":16,"unpackedSize":61028,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCID2oyc6jC/BtOT8pj5/A7uQ2mRGBXL6UWq5iFCAGraTmAiATsVntUuMQg9zEmQb0xpORWhqeQN62/7oCLa4/0CO5XQ=="}]},"_npmUser":{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"},"directories":{},"maintainers":[{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/llm-dsl_1.2.1-beta.10_1704890883684_0.9431951682105135"},"_hasShrinkwrap":false},"1.2.1-beta.11":{"name":"@k-apps.io/llm-dsl","version":"1.2.1-beta.11","description":"a DSL for Large Language Models (LLM)","main":"dist/index.js","scripts":{"test":"jest","build":"tsc","deploy-prod":"npm run build && npm publish --access public","beta":"npm run build && git add . && git commit -m \"Committing changes before beta deploy\" && npm version prerelease --preid=beta && node prePublish.js && npm publish --tag beta ","deploy":"npm run build && git add . && git commit -m \"Committing changes before beta deploy\" && npm version prerelease --preid=beta && npm publish --tag beta"},"repository":{"type":"git","url":"git+https://github.com/k-apps-io/llm-dsl.git"},"keywords":["LLM","chatgpt","openai"],"author":{"name":"@imnotakopp"},"license":"ISC","bugs":{"url":"https://github.com/k-apps-io/llm-dsl/issues"},"homepage":"https://github.com/k-apps-io/llm-dsl#readme","devDependencies":{"@k-apps.io/llm-dsl-chatgpt":"^1.2.1-beta.0","@types/jest":"^29.5.1","@types/lodash":"^4.14.200","@types/uuid":"^9.0.6","dotenv":"^16.3.1","jest":"^29.5.0","openai":"^4.14.2","tiktoken":"^1.0.10","ts-jest":"^29.1.0","ts-node":"^10.9.1","typescript":"^5.2.2"},"dependencies":{"ebnf":"^1.9.1","json5":"^2.2.3","lodash":"^4.17.21","uuid":"^9.0.1"},"types":"./dist/index.d.ts","readme":"# todo's\nalign the CommandFunction arguments as a spreadable object\nupdate functions to resolve a string and set the role / visibility to System\nhave a stop call which resolves to then\nhave a condition series like branch\nbranch into sidebars for concurrent processing?\nload a module of functions to add to the chat\n\n# @k-apps.io/llm-dsl\nthis is a DSL for interacting with a LLM, chaining commands together to formulate robust tasks\n\n# Installation\n\n```shell\nnpm install @k-apps.io/llm-dsl\n```\n\npair this with an LLM implementation such as\n\n[@k-apps.io/llm-dsl-chatgpt](https://www.npmjs.com/package/@k-apps.io/llm-dsl-chatgpt)\n\n# Usage\n\n### Hello World\n```javascript\nimport { LLM, DSL, LocalStorage, Rules, CODE_BLOCK_RULE } from \"@k-apps.io/llm-dsl\";\nimport { ChatGPT, Options } from \"@k-apps.io/llm-dsl-chatgpt\";\n\nrequire( \"dotenv\" ).config();\n\nconst main = () => {\n\n  const LLM = new ChatGPT({});\n\n  type Context = string[];\n\n  const chat = new DSL<Options, Context>( {\n    llm: LLM,\n    storage: LocalStorage,\n    options: {\n      model: \"gpt-4\"\n    }\n  } );\n\n  chat\n    .rule( CODE_BLOCK_RULE )\n    .prompt( {\n      message: \"Hello!\"\n    } )\n    .prompt( {\n      message: 'send me 3 progamming languages as a JSON object e.g ```json [\"lang1\", \"lang2\", \"langN\"]```',\n    } )\n    .expect( ( response ) => new Promise<void>( ( resolve, reject ) => {\n      // setting expectations for the response\n      if ( response.codeBlocks === undefined ) {\n        reject( \"expecting a json code block\" );\n      } else if ( response.codeBlocks.length > 1 ) {\n        reject( \"only 1 code block was requested\" );\n      } else {\n        const block = response.codeBlocks[ 0 ];\n        if ( block.lang !== \"json\" ) {\n          reject( `expecting a json code block, found '${ block.lang }' instead` );\n        } else {\n          const data = JSON.parse( block.code );\n          // setting the context in this command makes it available hereafter\n          chat.context = data;\n          resolve();\n        }\n      }\n    } ) )\n    .promptForEach( ( languages: Context ) => {\n      // using this context I can send more prompts\n      return languages.map( language => {\n        const message = `write me a hello world progamming in ${ language }`;\n        return { message: message };\n      } );\n    } )\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then( () => {\n      console.log( \"done\" );\n    } )\n    .catch( error => {\n      console.error( error );\n    } );\n}\n```\n\n# Rules\na `rule` represents a specific instruction for the LLM and `you` to follow. Regardless where you define these in the chain of commands they will always be included at the top of the convesation with `Visibility.REQUIRED`\n\n```javascript\nimport { DSL, Rule } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    .rule({\n      name: \"Bob Uecker\",\n      requirement: \"respond in the style of Bob Uecker, the Famous Radio Broadcaster and not so Fammous American League Baseball Player\"\n    })\n    .prompt({\n      message: \"Uecke, give me a home run call\"\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content );\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Functions\n\nIn your DSL script, you can define custom `functions` that the Language Model (LLM) may call, and the results will be returned as new `Message` objects. This feature allows you to create custom logic and interactions within your conversation with the LLM.\n\n```javascript\nimport { DSL, Rule, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    .function<{ unit?: \"Fahrenheit\" | \"Celsius\"; }>( {\n      name: \"getTheCurrentWeather\",\n      description: \"helps the AI model determine the current weather\",\n      parameters: {\n        \"type\": \"object\",\n        \"properties\": {\n          \"unit\": {\n            \"type\": \"string\",\n            \"enum\": [ \"Fahrenheit\", \"Celsius\" ]\n          }\n        }\n      },\n      func: ( { unit = \"Fahrenheit\", context } ) => {\n        return new Promise( ( resolve, reject ) => {\n          const weather = {\n            temperature: unit === \"Fahrenheit\" ? 24 : 5,\n            unit: unit,\n            percipitation: \"it's snowing\"\n          };\n          resolve( {\n            message: `here is the current weather report \\`\\`\\`json ${ JSON.stringify( weather ) }\\`\\`\\``\n          } );\n        } );\n      }\n    } )\n    .rule( {\n      name: \"Temperature\",\n      requirement: \"all temperature readings must be in Celsius\"\n    } )\n    .prompt( {\n      message: \"what's the weather like today?\"\n    } )\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content );\n    } )\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Prompting\na `prompt` is the main form of interacting with the LLM, use this command to send a message and generate a response\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL<Options, string>({});\n  chat.context = \"what is something the bank has that could be used on the football field?\";\n  chat\n    .prompt({\n      message: \"Why did the football team go to the bank?\"\n    })\n    .prompt( context => {\n      // a prompt can also access the context of the chat via a `CommandFunction`\n      return {\n        message: `here is a hint: ${context}`\n      }\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n# Context\n\nthe `context` is a shared space that enables the seamless exchange of information between different commands and prompts. It's a vital tool for tasks that involve gathering, adjusting, or retrieving data across multiple interactions. The `context` ensures your conversation flows cohesively and empowers you to manage and transfer artifacts effortlessly.\n\nTo manage the `context`, you can use commands like [response](#response) and [expect](#expect) or call `setContext` directly\n\nTo use the `context`, checkout [prompt](#prompting), [promptForEach](#promptforeach) and [branchForEach](#branchforeach)\n\n\n## response\n`response` provides direct accesss the response of a prompt, it gives you control to continue the chain of commands by calling `resolve` or stop by calling `reject`\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL<Options, string>({});\n  chat\n    .prompt({\n      message: \"finish the sentence 'one does not'\"\n    })\n    .response( response => new Promise<void>(( resolve, reject ) => {\n      if ( response.content.includes(\"Mordor\")) {\n        chat.context = {\n          isChill: true,\n        }\n      } else {\n        chat.context = {\n          isChill: false,\n          tasks: [ \"watch LOTR\" ]\n        }\n      }\n      resolve();\n    }))\n    .prompt( ( context, $this ) => {\n      if ( context.isChill ) {\n        return {\n          message: \"great answer\"\n        }\n      } else {\n        return {\n          message: \"I need you to watch LOTR\"\n        }\n      }\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n## expect\nThe `expect` function is another way to access the response of a prompt and set expectations for it. When working with the Language Model (LLM), you can use this command to define specific criteria that the response should meet. If these criteria are not met, you have the option to call `reject(\"with your reason\")` to initiate a [dispute resolution](#dispute-resolution) process. On the other hand, if the response aligns with your expectations, you can use the `resolve()` method to continue the chain of interactions.\n\nThis command plays a pivotal role in ensuring that the Language Model's responses are in line with your desired outcomes. It serves as a valuable tool for quality control and enables the creation of custom interactions with the model. By setting clear expectations and handling responses accordingly, you can shape the behavior of the model to meet your specific needs.\n\n```javascript\nimport { DSL, Options } from \"@k-apps.io/llm-dsl\";\n\ninterface Club {\n  club: string;\n  yardage: number;\n}\n\nconst main = () => {\n  const chat = new DSL<Options, Club[]>({});\n  chat\n    .prompt({\n      message: \"please generate a JSON Array of golf clubs I could hit out of the ruff. This JSON array must structured as ```javascript {\\noptions: { club: string; yardage: number; }[]\\n}``` and be provided as a JSON code block\"\n    })\n    .expect( response => new Promise<void>(( resolve, reject ) => {\n      // I have high expecations\n      if ( response.codeBlocks === undefined ) {\n        reject(\"a json code block was expected\")\n      } else if ( response.codeBlocks.length > 1 ) {\n        reject(\"too many code blocks, only 1 is required\")\n      } else {\n        const block = response.codeBlocks[0];\n        if ( block.lang !== \"json\" ) {\n          reject(\"code block must be 'json'\")\n        } else {\n          const data = JSON.parse(block.code);\n          chat.context = data.options;\n          resolve();\n        }\n      }\n    }))\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n#### Dispute Resolution\n\nWhen you call `reject(\"with your reason\")`, the DSL will initiate a `sidebar` chat to resolve the dispute between your expectations and the prior response. This `sidebar` chat retains the `conetxt`, `rules` and `functions` you originally defined in the main chat.\n\nEach response generated in the `sidebar` will be assessed against your expectations until they are met or the maximum call stack limit is reached. \n\n> Adjust the expect max call stack with `setMaxCallStack`\n\nThe response that aligns with your expectations will be marked with `Visibility.OPTIONAL` and will follow the response that triggered the dispute. The initial response that led to the dispute will be updated with a visibility of `Visibility.HIDDEN`, and the chain of commands will resume.\n\n## promptForEach\nThe `promptForEach` command allows you to use the chat `context` to generate one or more prompts, each of which is executed sequentially and synchronously. This function is particularly useful when you need to create multiple prompts dynamically based on the context data.\n\n\n```javascript\nimport { DSL } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const chat = new DSL({});\n  chat\n    /** continued from above */\n    .promptForEach( ( context: Club[] ) => {\n      return context.map(({ club, yardage}, index ) => {\n        return {\n          message: `I expect the ruff to take about 40% off the distance of the club, what is the new yardarge for ${club}?`\n        }\n      })\n    })\n    .prompt({\n      message: \"I have 120y to the hole and I want to carry it 115y, which club should I use?\"\n    })\n    .prompt({\n      message: \"would this be a full swing?\"\n    })\n    .prompt({\n      message: \"I stuck it close, let's get some birds\",\n    })\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then(() => console.log(\"done\"))\n    .catch(console.error)\n}\n\nmain();\n```\n\n## branchForEach\n`branchForEach` similary enables you to use the chat `context` to generate a series of commands. The series is scoped to each command between the `branchForEach` and the next `join` command. Each series is executed sequentially and synchronously.\n\n```javascript\nimport { DSL } from \"@k-apps.io/llm-dsl\";\n\nconst main = () => {\n  const LLM = new ChatGPT( {} );\n\n  const chat = new DSL<PromptOptions, Context>( {\n    llm: LLM,\n    storage: LocalStorage,\n    options: {\n      model: \"gpt-4\",\n      temperature: 0.5,\n      top_p: 0.5\n    }\n  } );\n\n  chat\n    .rule( CODE_BLOCK_RULE )\n    .prompt( {\n      message: \"generate a list of cities implementing the ```typescript interface { cities: string[] }``` as a json code block\"\n    } )\n    .expect( response => {\n      // todo - set the expectations\n      const data = JSON.parse( response.codeBlocks![0].code );\n      chat.context = data.cities;\n      resolve();\n    })\n    .branchForEach( context => {\n      return context.map( city => {\n        return {\n          message: `consider the following city, ${ city } and pick a monument, artificat or famous person in this city we'll call this the target`\n        };\n      } );\n    } )\n    .prompt( {\n      message: \"consider the RPG table top game Sprawl, create a new mission in this city that involves our target\"\n    } )\n    .response( response => {\n      // write the response to a file\n      return new Promise<void>(( resolve, reject ) => {\n        writeFileSync(`./sprawl-missions/${response.id}.txt`);\n        resolve();\n      })\n    })\n    .join()\n    .stream( chunk => {\n      if ( chunk.type === \"message\" ) process.stdout.write( chunk.content )\n    })\n    .then( () => {\n      console.log( \"done\" );\n    } )\n    .catch( error => {\n      console.error( error );\n    } );\n};\n\nmain();\n\n```\n\n# Storage\nchats can be stored for later use; this requires a `storage` mechanism. This mechanism manages saving and retrieving chats from a data store. This package includes `LocalStorage` which will save the chats to the local file system and `NoStorage` which does not save any chats. \n\n`LocalStorage` requires an environment variable `CHATS_DIR` where the chats will be stored as `{id}.json` files \n> CHATS_DIR will not be created automatically\n```text\n# .env\nCHATS_DIR=/opt/chats\n```\n\n\n## Custom\nyou can define your own storage mechanism by implementing the `Storage` interface\n\n```javascript\nimport { ChatStorage } from \"@k-apps.io/llm-dsl\";\n\nconst MyAPI: ChatStorage = {\n  getById: ( id: string ): Promise<Chat> => {\n    return new Promise<Chat>( ( resolve, reject ) => {\n      /**\n       * TODO: retrieve an existing chat\n       */\n      reject();\n    } );\n  },\n  save: ( chat: Chat ): Promise<void> => {\n    return new Promise<void>( ( resolve, reject ) => {\n      /**\n       * TODO: save the chat\n       */\n      reject();\n    } );\n  }\n}\n\nconst main = () => {\n  const chat = new DSL({\n    storage: MyAPI,\n    // ...\n  });\n}\n\nmain();\n```\n","readmeFilename":"README.md","gitHead":"d4732fbe128305cdb2b0fea9cca6f479209e4dfc","_id":"@k-apps.io/llm-dsl@1.2.1-beta.11","_nodeVersion":"18.13.0","_npmVersion":"9.2.0","dist":{"integrity":"sha512-Ji0dxDZxeYGAyd+USFJB9RdG7yblp01M63ZZEMOmZRZBqHiPh7ay12WAXCRZRxTUFDJKCI5Wk/1ZR0NOSgaJuQ==","shasum":"324a1fe39a7ecff5687f50da1caf4f4ca9fb2d6a","tarball":"https://registry.npmjs.org/@k-apps.io/llm-dsl/-/llm-dsl-1.2.1-beta.11.tgz","fileCount":16,"unpackedSize":61217,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDUNE6wF3I6xdQThhnO/Y+etdOCmukZq0dngQB4VOWJbQIhALtxebtt7FofUpGCfVXRvr0ez2l4hNAtmfSyL0PnXPFU"}]},"_npmUser":{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"},"directories":{},"maintainers":[{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/llm-dsl_1.2.1-beta.11_1704891271759_0.03224653197017702"},"_hasShrinkwrap":false},"1.3.0":{"name":"@k-apps.io/llm-dsl","version":"1.3.0","description":"a DSL for Large Language Models (LLM)","main":"dist/index.js","scripts":{"test":"jest","build":"tsc","deploy":"npm run build && npm publish --access public"},"repository":{"type":"git","url":"git+https://github.com/k-apps-io/llm-dsl.git"},"keywords":["LLM","chatgpt","openai"],"author":{"name":"@imnotakopp"},"license":"ISC","bugs":{"url":"https://github.com/k-apps-io/llm-dsl/issues"},"homepage":"https://github.com/k-apps-io/llm-dsl#readme","devDependencies":{"@k-apps.io/llm-dsl-chatgpt":"^1.0.7","@types/jest":"^29.5.1","@types/lodash":"^4.14.200","@types/uuid":"^9.0.6","dotenv":"^16.3.1","jest":"^29.5.0","openai":"^4.14.2","tiktoken":"^1.0.10","ts-jest":"^29.1.0","ts-node":"^10.9.1","typescript":"^5.2.2"},"dependencies":{"ebnf":"^1.9.1","lodash":"^4.17.21","uuid":"^9.0.1"},"_id":"@k-apps.io/llm-dsl@1.3.0","gitHead":"26ee6f82bf63dd5b99cfd948654bb02b07878a39","types":"./dist/index.d.ts","_nodeVersion":"21.7.1","_npmVersion":"10.5.0","dist":{"integrity":"sha512-/qIkSpHBD4Lh5ShqX7Ke1VvwtrzdCOlc9knqmY3UwrQ9L2w3VTB6GK783pYa2TXDh/EbwwkxmenuqDTLurh/Lw==","shasum":"f1af4d3cf92bd67fb30675ebf28b5ca4b3c70aa1","tarball":"https://registry.npmjs.org/@k-apps.io/llm-dsl/-/llm-dsl-1.3.0.tgz","fileCount":24,"unpackedSize":57181,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIG3Fu/hCrs1QEz3Uzvy2fYLORGIrmeNSkIs1lQN8M3tvAiEA8rsoc5yUOb2b05sLBlOWPZk9P1AGP01KQrj77CfwjXM="}]},"_npmUser":{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"},"directories":{},"maintainers":[{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/llm-dsl_1.3.0_1711308194753_0.6973751987098267"},"_hasShrinkwrap":false}},"maintainers":[{"name":"imnotakopp","email":"alan.kopp.1216@gmail.com"}],"description":"a DSL for Large Language Models (LLM)","homepage":"https://github.com/k-apps-io/llm-dsl#readme","keywords":["LLM","chatgpt","openai"],"repository":{"type":"git","url":"git+https://github.com/k-apps-io/llm-dsl.git"},"author":{"name":"@imnotakopp"},"bugs":{"url":"https://github.com/k-apps-io/llm-dsl/issues"},"license":"ISC","readme":"# Project Transition Notice\n\nVersion 2.0 has moved to a new space for better code and package management.\n\n**Old Package** [@k-apps.io/llm-dsl](https://www.npmjs.com/package/@k-apps.io/llm-dsl)\n\n**New Package:** [@k-apps-io/llm-dsl](https://www.npmjs.com/package/@k-apps-io/llm-dsl)\n\n**Important:** This package [@k-apps.io/llm-dsl](https://www.npmjs.com/package/@k-apps.io/llm-dsl) will be deleted no earlier than __2024-09-01__. Please make sure to migrate to [@k-apps-io/llm-dsl](https://www.npmjs.com/package/@k-apps-io/llm-dsl) before this date.\n\n\nThank you for your continued support!","readmeFilename":"README.md"}