Structured Output
Gerbil can make a model return a validated JSON object instead of free-form text. It generates, extracts the JSON from the output, validates it against your schema, and — if validation fails — retries with a corrective nudge until the result is valid or the retry budget runs out. Because on-device tokens are free, re-rolling malformed JSON costs nothing but a moment, so you get a typed object back rather than a string you have to defensively parse.
useObject in the browser
The useObject hook is the React entry point. Type it with the shape you expect, call generate with a prompt and a schema, and read the parsed result from object:
01"use client";02
03import { useObject } from "@tryhamster/gerbil/hooks";04
05type Person = { name: string; age: number };06
07function Extractor() {08 const { object, generate, attempts, isGenerating } = useObject<Person>();09
10 return (11 <div>12 <button13 onClick={() =>14 generate('Extract {name, age} from: "I am Sarah, 28"', {15 schema: { required: ["name", "age"] },16 })17 }18 disabled={isGenerating}19 >20 {isGenerating ? "Extracting…" : "Extract"}21 </button>22 {object && (23 <p>24 {object.name} is {object.age} (took {attempts} attempt25 {attempts === 1 ? "" : "s"})26 </p>27 )}28 </div>29 );30}The hook returns the parsed object (null until one is produced), attempts (1 means it parsed on the first try), plus the usual isGenerating, isLoading, isReady, stop, and error lifecycle fields.
How validation works
The schema option is an ObjectValidator. It can take one of three forms:
- —A minimal schema object —
{ required: ["name", "age"] }. Every key inrequiredmust exist on the parsed object. - —A predicate —
(o) => boolean. Full control: returntruewhen the parsed value is acceptable. - —Omitted — any syntactically valid JSON value (object or array) is accepted.
Use a predicate when key presence isn't enough and you need to check types or values:
01"use client";02
03import { useObject } from "@tryhamster/gerbil/hooks";04
05type Primes = { primes: number[] };06
07function Primes() {08 const { object, generate, isGenerating } = useObject<Primes>();09
10 return (11 <div>12 <button13 disabled={isGenerating}14 onClick={() =>15 generate("List the first 3 primes as {primes: number[]}", {16 // Predicate: enforce the field is an array of numbers.17 schema: (o) =>18 Array.isArray(o.primes) &&19 o.primes.every((n) => typeof n === "number"),20 maxRetries: 6,21 })22 }23 >24 Generate25 </button>26 {object && <pre>{JSON.stringify(object)}</pre>}27 </div>28 );29}maxRetries (default 4) is the number of retries after the first attempt, so up to maxRetries + 1 generations run before it gives up.
Imperatively with generateObject
When you need structured output outside React — or alongside other capabilities on a shared engine — call generateObject on the engine. It runs the same generate → parse → validate → retry loop and returns the parsed object plus the attempt count:
01import { useEngine } from "@tryhamster/gerbil/hooks";02
03type Person = { name: string; age: number };04
05function Extractor() {06 const { generateObject, isReady } = useEngine();07
08 const extract = async () => {09 const { object, attempts } = await generateObject<Person>(10 'Extract {name, age} from: "I am Sarah, 28"',11 { schema: { required: ["name", "age"] }, maxRetries: 4 }12 );13 console.log(object, "in", attempts, "attempt(s)");14 // { name: "Sarah", age: 28 }15 };16
17 return (18 <button onClick={extract} disabled={!isReady}>19 Extract20 </button>21 );22}generateObject extends the engine's generation options, so maxTokens, system, and temperature all apply. A low temperature (e.g. 0.2) tends to make extraction more reliable.
Types
The structured-output types are exported from @tryhamster/gerbil/hooks if you want to annotate validators or options yourself:
01import type {02 ObjectValidator,03 GenerateObjectOptions,04} from "@tryhamster/gerbil/hooks";05
06// A reusable typed validator.07const isPerson: ObjectValidator<{ name: string; age: number }> = (o) =>08 typeof o.name === "string" && typeof o.age === "number";09
10const options: GenerateObjectOptions = {11 schema: isPerson,12 maxRetries: 4,13 temperature: 0.2,14};See also
- —React Hooks — the full hook set, including
useObjectin context. - —Tools — function calling when you need the model to invoke code, not just emit data.