"Extract a name and email from this sentence" is the easy 10% of structured output. The other 90% is everything that doesn't fit in one prompt, one turn, or one model call. Here are five things shapecraft handles once you're past the basics.
A single message rarely has everything you need. Someone books an appointment over three or four back-and-forth messages, not one. turnaround
mode lets the conversation run naturally and validates the whole transcript once, at the end, against one schema:
import { generate, openai } from "@aviasole/shapecraft";
const result = await generate(model, BookingSchema, conversationHistory, {
turnaround: true,
});
No manual "do I have everything yet?" tracking, no partial-state bugs, just one validated object once the conversation is actually complete.
Healthcare data has a standard (FHIR R4) and it's not optional if you're integrating with anything real. Built-in presets mean you're not hand-writing a Patient
or Observation
schema from scratch:
import { generate, openai } from "@aviasole/shapecraft/fhir";
import { PatientSchema } from "@aviasole/shapecraft/fhir";
const patient = await generate(openai({ model: "gpt-4o-mini" }), PatientSchema, clinicalNote);
Same retry/validation guarantees as any other schema, just pre-built to match a spec you'd otherwise have to implement yourself.
"Is this order still on hold?" isn't answerable from the prompt alone, it needs an actual lookup. generateWithTools()
lets the model call your functions, see the results, and then produce a validated final answer:
import { generateWithTools } from "@aviasole/shapecraft";
const result = await generateWithTools(model, [lookupOrder], AnswerSchema, userQuestion);
The tool call's arguments are validated before your function ever runs, and the final answer goes through the same schema check as a normal generate()
call.
Triage, then diagnose, then draft a response, each step needs the previous step's validated output, not its raw text. runAgents()
chains a sequence of generate()
calls, each with its own model and schema, threading validated data forward:
import { defineAgent, runAgents } from "@aviasole/shapecraft/agentic";
const triage = defineAgent({ model, schema: TriageSchema, role: "triage" });
const diagnose = defineAgent({ model, schema: DiagnosisSchema, role: "diagnose" });
const result = await runAgents([triage, diagnose], input);
Each step keeps its own retry behavior, there's no shared fragile state between them.
Sometimes the thing you need to validate isn't fresh model output, it's a file that already exists, maybe from a batch job, a CI pipeline, or someone else's export. The CLI does the same structural check generate()
uses internally, from the terminal:
npx shapecraft validate --schema schema.json --output output.json
Exits 0
and prints a check mark on success, exits 1
with the specific violation on failure. Handy for a CI step that shouldn't need its own validation script.
Multi-turn, healthcare presets, tool calling, agent pipelines, file validation, none of these are separate systems bolted on. They're all the same generate()
retry-and-validate core, pointed at a different shape of input or output.
We've published full documentation covering all of this (and everything from the last post too) at ** aviasoletechnologies.github.io/shapecraft** - worth a look if any of these five sound like your actual problem.