DEV Community

Cover image for 5 Things I've Actually Used shapecraft For
Yatin Davra
Yatin Davra

Posted on

5 Things I've Actually Used shapecraft For

Every structured-output library's README shows you the same demo: extract a name and age from a sentence, get back clean JSON. Cool, but that's not really a use case, that's a unit test.

Here's what I've actually reached for shapecraft for, across a handful of real projects. Five short examples, no fluff, so you can see where this fits before you commit to reading the full docs.

1. Turning support tickets into triageable data

A raw customer message ("hey my invoice from last month looks wrong and also the app crashed twice") isn't something you can route or prioritize as-is. Give it a schema instead:

import { generate, openai } from "@aviasole/shapecraft";
import { z } from "zod";

const TicketSchema = z.object({
  category: z.enum(["billing", "bug", "how-to", "account"]),
  priority: z.enum(["low", "normal", "urgent"]),
  summary: z.string(),
});

const result = await generate(openai({ model: "gpt-4o-mini" }), TicketSchema, rawMessage);
Enter fullscreen mode Exit fullscreen mode

One call, one typed object, ready to drop into a queue.

2. Parsing receipts, invoices, and other "almost structured" documents

OCR'd text from a receipt is a mess of line items, totals, and stray whitespace. Instead of writing regex to hunt for the total, describe the shape you want and let the model do the extraction:

const ReceiptSchema = z.object({
  vendor: z.string(),
  total: z.number(),
  lineItems: z.array(z.object({ name: z.string(), price: z.number() })),
});
Enter fullscreen mode Exit fullscreen mode

Same generate() call, same guarantees, just a different schema.

3. Slot-filling for a form-driven chatbot

Onboarding flows ("what's your name, email, and company size?") don't need a whole conversation engine, they need a bot that keeps asking until it has every required field, validated. shapecraft's generate() retry loop already does the "keep trying until it's valid" part, so the bot logic is just: ask, extract, check what's missing, repeat.

4. Running a local model with a real guarantee

Not everything can leave the device. For document classification that has to run fully offline, llamaCpp() applies a GBNF grammar at the token level, so the model cannot produce anything outside your five category labels, not "usually doesn't," actually can't:

import { llamaCpp } from "@aviasole/shapecraft";

const model = llamaCpp({ modelPath: "./models/llama-3.2-3b.gguf" });
const result = await generate(model, { gbnf: categoryGrammar }, chunk);
Enter fullscreen mode Exit fullscreen mode

That's the difference between constrained and best-effort guarantee levels, and it matters a lot more once you're running unattended over a few thousand files overnight.

5. Batch-processing a folder of anything

Classify, summarize, or extract from a directory of files without hand-rolling concurrency control:

import { generateBatch } from "@aviasole/shapecraft";

const results = await generateBatch(
  model,
  schema,
  files.map((f) => f.text),
  { concurrency: 5 }
);
Enter fullscreen mode Exit fullscreen mode

Each item validates and retries independently, so one bad file doesn't take down the batch.

That's the range

Ticket triage, document extraction, chatbot slot-filling, offline classification, batch jobs, all the same core idea: describe the shape you want, get back something you can actually trust. Check result.guaranteeLevel any time you want to know exactly how much.

One more thing: we've now published full documentation, with a guide for every feature mentioned here (and a bunch not covered in this post, like streaming, tool calling, and multi-agent orchestration). Go explore it at aviasoletechnologies.github.io/shapecraft.

Top comments (0)