{"_id":"@elg/coerce-llm-output","name":"@elg/coerce-llm-output","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@elg/coerce-llm-output","version":"0.1.0","main":"dist/index.js","types":"dist/index.d.ts","license":"MIT","repository":{"type":"git","url":"git+ssh://git@github.com/taneliang/coerce-llm-output.git"},"author":{"name":"E-Liang Tan","email":"eliang@eliangtan.com"},"scripts":{"clean":"rm -rf dist","build":"tsc --project tsconfig.dist.json","typecheck":"tsc --noEmit","test":"jest --coverage","lint":"yarn lint:code && yarn lint:misc","lint:fix":"yarn lint:code:fix && yarn lint:misc:fix","lint:code":"eslint src ./*.mjs","lint:code:fix":"yarn lint:code --fix","lint:misc":"prettier --check './*.{js,json,md}'","lint:misc:fix":"yarn lint:misc --write","prepare":"yarn clean && yarn build"},"dependencies":{"partial-json":"^0.1.7"},"peerDependencies":{"zod":"^3"},"devDependencies":{"@babel/core":"^7.24.7","@babel/preset-env":"^7.24.7","@babel/preset-typescript":"^7.24.7","@eslint/js":"^9.6.0","@types/eslint__js":"^8.42.3","@types/jest":"^29.5.12","babel-jest":"^29.7.0","eslint":"^9.6.0","eslint-config-prettier":"^9.1.0","jest":"^30.0.0-alpha.5","prettier":"^3.3.2","ts-dedent":"^2.2.0","typescript":"^5.5.3","typescript-eslint":"^7.16.0","zod":"^3.23.8"},"_id":"@elg/coerce-llm-output@0.1.0","gitHead":"2b853a49b3609dd80aedb85e82bea46cfbf3f143","description":"[![CircleCI](https://dl.circleci.com/status-badge/img/gh/taneliang/coerce-llm-output/tree/main.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/gh/taneliang/coerce-llm-output/tree/main) [![codecov](https://codecov.io/github/taneliang/coerce-l","bugs":{"url":"https://github.com/taneliang/coerce-llm-output/issues"},"homepage":"https://github.com/taneliang/coerce-llm-output#readme","_nodeVersion":"20.12.2","_npmVersion":"10.5.0","dist":{"integrity":"sha512-Gtv3MLrdiVJbcGzQWwl5kLw935Uj5BBZWixERqRRCqBSPfmu9xTBCHpcjoMU8KC7OTnRjHrhtnDXmagVFJdzXQ==","shasum":"d242b27656bd3bf7ff41edd01c01295a570183e8","tarball":"https://registry.npmjs.org/@elg/coerce-llm-output/-/coerce-llm-output-0.1.0.tgz","fileCount":15,"unpackedSize":23834,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIA0+akZ+23pMZTiQPV6eZCJIh4dCkUjp/h1qafj/BNWAAiBgRzgK6iOtNjvGzm7GC4B6Ul2+s2/A79Emp7J5mlCvJw=="}]},"_npmUser":{"name":"elg","email":"eliang@eliangtan.com"},"directories":{},"maintainers":[{"name":"elg","email":"eliang@eliangtan.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/coerce-llm-output_0.1.0_1720594354265_0.4204600892705874"},"_hasShrinkwrap":false}},"time":{"created":"2024-07-10T06:52:34.131Z","0.1.0":"2024-07-10T06:52:34.544Z","modified":"2024-07-10T06:52:34.834Z"},"maintainers":[{"name":"elg","email":"eliang@eliangtan.com"}],"description":"[![CircleCI](https://dl.circleci.com/status-badge/img/gh/taneliang/coerce-llm-output/tree/main.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/gh/taneliang/coerce-llm-output/tree/main) [![codecov](https://codecov.io/github/taneliang/coerce-l","homepage":"https://github.com/taneliang/coerce-llm-output#readme","repository":{"type":"git","url":"git+ssh://git@github.com/taneliang/coerce-llm-output.git"},"author":{"name":"E-Liang Tan","email":"eliang@eliangtan.com"},"bugs":{"url":"https://github.com/taneliang/coerce-llm-output/issues"},"license":"MIT","readme":"# @elg/coerce-llm-output\n\n[![CircleCI](https://dl.circleci.com/status-badge/img/gh/taneliang/coerce-llm-output/tree/main.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/gh/taneliang/coerce-llm-output/tree/main)\n[![codecov](https://codecov.io/github/taneliang/coerce-llm-output/graph/badge.svg?token=Vry55xt2Xs)](https://codecov.io/github/taneliang/coerce-llm-output)\n\n`coerceLlmOutput` makes it possible to use LLM output in a typesafe way by coercing it into a well-typed and validated JSON object or array.\n\n## Installation\n\n```sh\nnpm install @elg/coerce-llm-output zod\n```\n\nOR\n\n```sh\nyarn add @elg/coerce-llm-output zod\n```\n\n## Usage\n\n### Basic usage with OpenAI\n\n```typescript\nimport { coerceLlmOutput } from \"@elg/coerce-llm-output\";\nimport OpenAI from \"openai\";\nimport { z } from \"zod\";\n\nconst openai = new OpenAI();\n\nconst User = z.object({\n  id: z.string(),\n  name: z.string(),\n  email: z.string(),\n});\n\nasync function main() {\n  const chatCompletion = await openai.chat.completions.create({\n    model: \"gpt-3.5-turbo\",\n    messages: [\n      {\n        role: \"user\",\n        content:\n          \"Generate a JSON user object with the shape \" +\n          \"{ id: string; name: string; email: string }\",\n      },\n    ],\n  });\n\n  const output: z.infer<User> = coerceLlmOutput(\n    chatCompletion.choices[0].message,\n    User,\n  );\n}\n\nmain();\n```\n\n### Streaming\n\n```typescript\nimport { coerceLlmOutput } from \"@elg/coerce-llm-output\";\nimport OpenAI from \"openai\";\nimport { z } from \"zod\";\n\nconst openai = new OpenAI();\n\nconst User = z.object({\n  id: z.string(),\n  name: z.string(),\n  email: z.string(),\n});\n\nasync function main() {\n  const stream = await openai.chat.completions.create({\n    model: \"gpt-3.5-turbo\",\n    messages: [\n      {\n        role: \"user\",\n        content:\n          \"Generate a JSON user object with the shape \" +\n          \"{ id: string; name: string; email: string }\",\n      },\n    ],\n    stream: true,\n  });\n  for await (const chunk of stream) {\n    const output: z.infer<User> = coerceLlmOutput(\n      chunk.choices[0].message, // TODO: Check if this works? Otherwise use chunk.choices[0].delta.content\n      User,\n    );\n    // process.stdout.write(chunk.choices[0]?.delta?.content || '');\n  }\n}\n\nmain();\n```\n\n### Arrays\n\n```typescript\nimport { coerceLlmOutput } from \"@elg/coerce-llm-output\";\nimport OpenAI from \"openai\";\nimport { z } from \"zod\";\n\nconst openai = new OpenAI();\n\nconst User = z.object({\n  id: z.string(),\n  name: z.string(),\n  email: z.string(),\n});\n\nasync function main() {\n  const chatCompletion = await openai.chat.completions.create({\n    model: \"gpt-3.5-turbo\",\n    messages: [\n      {\n        role: \"user\",\n        content:\n          \"Generate a JSON array of users. Each user must have the shape \" +\n          \"{ id: string; name: string; email: string }\",\n      },\n    ],\n  });\n\n  const output: z.infer<User>[] = coerceLlmOutput(\n    chatCompletion.choices[0].message,\n    z.array(User),\n  );\n}\n\nmain();\n```\n\n## Functionality\n\n`coerceLlmOutput`:\n\n1. Extracts JSON-like content.\n2. Parses incomplete JSON (using the excellent [partial-json](https://www.npmjs.com/package/partial-json)).\n3. Fixes up keys in the parsed JSON object to match the keys in the zod schema. We do this because LLMs sometimes generate camelCased or snake_cased, or wrongly cased keys unexpectedly.\n4. Parses the JSON object using the provided zod schema.\n","readmeFilename":"README.md"}