{"slug": "show-hn-jev-schema-builder-from-typescript-type-with-typia", "title": "Show HN: Jev schema builder from TypeScript Type with Typia", "summary": "Typia released a schema builder that generates Jev question schemas from TypeScript types, using property and enum comments to supply instructions and choice descriptions. The tool exposes typia.llm.evaluation<ITicketTriage>() to build a question map and answer decoder at compile time, and can be called directly via @typesafe-ai/sdk, through Vercel AI SDK's TypeSafe provider with experimental_decide(), or used with OpenAI Structured Outputs. The Jev examples target model jev-1.13.0 and require TYPESAFE_API_KEY for the direct SDK or TYPESAFE_AI_API_KEY for the Vercel AI SDK provider.", "body_md": "## TL;DR\n\n**Generate Jev question schemas from TypeScript types.** Property and enum comments supply the instructions and choice descriptions.\n**Call Jev directly or through Vercel AI SDK.** Decode the answers back into the declared TypeScript type.\n**Use the same type with OpenAI.** Generate JSON Schema for Structured Outputs and call the official `openai` SDK.\n\n``` python\nimport typia, { tags } from \"typia\";\n \nenum Department {\n  /**\n   * Payments, invoicing, refunds.\n   *\n   * Duplicate charges, failed cards, and plan changes belong here, even when\n   * the customer also mentions a bug.\n   *\n   * @probability 0.3\n   */\n  billing = \"billing\",\n \n  /**\n   * Bugs, outages, and broken integrations.\n   *\n   * Paging an engineer is expensive, so only pick this when the customer\n   * describes the product misbehaving.\n   *\n   * @probability 0.5\n   */\n  technical = \"technical\",\n \n  /**\n   * Pricing, upgrades, and new accounts.\n   *\n   * @probability 0.2\n   */\n  sales = \"sales\",\n}\n \ninterface ITicketTriage {\n  /**\n   * Does the customer convey urgency?\n   *\n   * A deadline, an outage, lost revenue, or a threat to leave counts. An\n   * impatient tone alone does not.\n   */\n  urgent: boolean;\n \n  /**\n   * Which team should handle this ticket?\n   *\n   * Decide by what the customer needs done, not by the words they use.\n   */\n  department: Department;\n \n  /**\n   * Does the customer ask for their money back?\n   *\n   * Complaining about a charge is not a request. The customer has to ask.\n   */\n  refund: boolean & tags.Probability<0.8>;\n}\n \nconst triage = typia.llm.evaluation<ITicketTriage>();\nconst ticket = \"I was charged twice this morning. Refund it now, or I leave.\";\n```\n\n`typia.llm.evaluation<ITicketTriage>()` generates a question map and answer decoder at compile time. Boolean properties become yes/no questions; `Department` becomes a choice question. Property comments supply instructions, and enum member comments describe each option.\n\nThe Jev examples below send `triage.questions` with the same `ticket`. Jev performs the inference; `triage.decode()` checks its answers and returns the reconstructed `ITicketTriage` in `result.data`, or validation errors in `result.errors`.\n\n``` js\nimport { TypeSafeClient } from \"@typesafe-ai/sdk\";\nimport { toJevQuestions } from \"@typia/jev\";\n \nconst client = new TypeSafeClient(); // reads TYPESAFE_API_KEY\nconst { answers } = await client.systemOne({\n  model: \"jev-1.13.0\",\n  state: ticket,\n  questions: toJevQuestions(triage.questions),\n});\n \nconst result = triage.decode(answers);\nif (result.success) {\n  result.data.department; // Department\n  result.data.refund; // boolean\n}\n```\n\n`@typesafe-ai/sdk` calls Jev directly. `toJevQuestions()` converts the yes/no question type from `\"boolean\"` to Jev’s native `\"noul\"`; choice and score questions pass through unchanged. Set `TYPESAFE_API_KEY` for the client.\n\nPass the returned `answers` to `triage.decode()`. The decoded object contains the fields declared in `ITicketTriage`; keep the original answer map if you also need Jev’s probabilities.\n\n``` js\nimport { typeSafeAi } from \"@ai-sdk/typesafe-ai\";\nimport { experimental_decide } from \"ai\";\n \nconst response = await experimental_decide({\n  model: typeSafeAi.decisionModel(\"jev-1.13.0\"),\n  state: ticket,\n  questions: triage.questions,\n});\nconst result = triage.decode(response.answers);\nif (result.success) {\n  result.data.department; // Department\n  result.data.refund; // boolean\n}\n```\n\nVercel AI SDK’s [TypeSafe provider](https://ai-sdk.dev/providers/ai-sdk-providers/typesafe-ai)  calls Jev through `experimental_decide()`. Pass `triage.questions` directly: the provider converts them to Jev’s native format. Decode `response.answers` with the same `triage.decode()` function.\n\nSet `TYPESAFE_AI_API_KEY` for this provider, rather than the direct SDK’s `TYPESAFE_API_KEY`. The example uses AI SDK 7’s decision API.\n\n``` python\nimport OpenAI from \"openai\";\n \nconst client = new OpenAI(); // reads OPENAI_API_KEY\nconst output = typia.llm.structuredOutput<ITicketTriage, { strict: true }>();\nconst response = await client.responses.create({\n  model: \"gpt-5.6-luna\",\n  input: [\n    { role: \"system\", content: \"Classify the customer ticket using the schema descriptions.\" },\n    { role: \"user\", content: ticket },\n  ],\n  text: {\n    format: {\n      type: \"json_schema\",\n      name: \"ticket_triage\",\n      strict: true,\n      schema: { ...output.parameters },\n    },\n  },\n});\n \nif (response.status !== \"completed\" || !response.output_text) {\n  throw new Error(\"OpenAI did not return a completed structured response.\");\n}\nconst result = output.validate(JSON.parse(response.output_text));\nif (result.success) {\n  result.data.department; // Department\n  result.data.refund; // boolean\n}\n```\n\nOpenAI’s official [`openai`](https://developers.openai.com/api/docs/guides/structured-outputs) SDK accepts JSON Schema for Structured Outputs. `typia.llm.structuredOutput<ITicketTriage, { strict: true }>()` generates that schema for `text.format` from the same type and comments. Set `OPENAI_API_KEY` for the client.\n\nOpenAI returns the triage object as JSON text. The guard handles incomplete responses or refusals without text; `output.validate()` checks the parsed object. This path does not return Jev’s answer map or enforce the evaluation-specific probability annotations.\n\n```\nnpm install -D ttsc typescript\nnpm install typia @typia/jev\n \nnpx ttsc              # build\nnpx ttsx src/index.ts # or run\n```\n\nInstall the SDK packages for the example you use. Save the shared declarations and one SDK example in `src/index.ts`, and set its API key. Use `ttsc` or `ttsx` to apply typia’s compile-time transform.", "url": "https://wpnews.pro/news/show-hn-jev-schema-builder-from-typescript-type-with-typia", "canonical_source": "https://typia.io/blog/jev/", "published_at": "2026-10-06 17:42:48+00:00", "updated_at": "2026-10-06 17:50:17.216837+00:00", "lang": "en", "topics": ["ai-tools", "developer-tools", "large-language-models", "structured-data"], "entities": ["Typia", "Jev", "TypeScript", "Vercel AI SDK", "OpenAI", "@typesafe-ai/sdk", "experimental_decide", "jev-1.13.0"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/show-hn-jev-schema-builder-from-typescript-type-with-typia", "markdown": "https://wpnews.pro/news/show-hn-jev-schema-builder-from-typescript-type-with-typia.md", "text": "https://wpnews.pro/news/show-hn-jev-schema-builder-from-typescript-type-with-typia.txt", "jsonld": "https://wpnews.pro/news/show-hn-jev-schema-builder-from-typescript-type-with-typia.jsonld"}}