hashbrown

Skillet schemas

A schema is a contract for values: which fields exist, what each field means, and which types are allowed. Hashbrown uses schemas for tool arguments, structured responses, and component inputs. Keeping that contract beside the feature makes the model’s options and the application’s expectations easier to compare.

Skillet is exported as s from @hashbrownai/core. Its schemas also infer TypeScript types, so you can describe a value once and reuse that description in your UI. Use descriptions to explain meaning, constraints to bound values, and ordinary application checks for facts a schema cannot establish, such as whether a delivery date is available.

One schema language, several contracts

Scroll horizontally to see the full diagram, or expand it.

Skillet schemaFields, types,descriptionsTool argumentsContract for a callStructured responseContract for returned dataComponent inputsContract for generatedprops
A schema describes a value. Application code still checks facts, permissions, and business rules.
Read the diagram

Skillet schemas describe tool arguments, structured response values, and exposed component inputs. The same definitions infer TypeScript types for application code; they do not establish domain facts.

One schema language, several contracts

A schema describes a value. Application code still checks facts, permissions, and business rules.

Read the diagram

Skillet schemas describe tool arguments, structured response values, and exposed component inputs. The same definitions infer TypeScript types for application code; they do not establish domain facts.

Describe an application contract

Create reading-schema.ts in an application with @hashbrownai/core installed. This module has no provider or framework dependency.

import { s } from '@hashbrownai/core';

export const readingRequestSchema = s.object('A book request', {
  title: s.string('The requested book title'),
  copies: s.integer('Copies requested', { minimum: 1, maximum: 20 }),
  format: s.enumeration('Preferred edition', {
    paperback: 'A physical paperback edition',
    ebook: 'A digital edition',
  }),
});

export type ReadingRequest = s.Infer<typeof readingRequestSchema>;

export const exampleRequest: ReadingRequest = {
  title: 'Pride and Prejudice',
  copies: 2,
  format: 'paperback',
};

Pass readingRequestSchema as the schema option of useStructuredChat(). The structured output example supplies the complete framework component and Quickstart prerequisites. Use the inferred type for application code; a TypeScript annotation alone does not validate untrusted JSON at runtime.

The copy bound describes allowed values. It cannot tell us whether two copies exist or whether this user can reserve them. Check those facts at the action boundary. If a required field may be unknown, model that possibility explicitly rather than relying on an instruction to produce an invalid object.

Explore it in the workbench

The workbench below replays a recorded run of this lesson.

Inspect the shared plan contract, preview validation and explicit acceptance across two turns.

Recorded in the real React workbench with OpenAI streaming requests and the actual Hashbrown SDK. Unchanged provider events paced at 20 ms for filming. Footage slowed to half speed for readability. Replay reads captured frames and does not make requests or writes. No narration. Read the text walkthrough.

Open full workbench
Open full workbench

A schema describes the data our application expects. Let’s use the same two-step plan: a title and an array of steps. We can use this contract for typed model output and inspect it beside the values Hashbrown resolves.

Skillet is exported as s from @hashbrownai/core. Its TypeScript API describes response shapes, tool arguments, and trusted component props, including which values can appear while output streams.

Run the workbench

Use the panel above, or run the workbench on your machine.

Describe the plan

Both frameworks and the server use this shared schema:

runtime/workbench.ts
export const workbenchPlanSchema = s.object('A two-step plan proposal', {
  title: s.string('A concise plan title'),
  steps: s.streaming.array(
    'Exactly two concise steps',
    s.string('A clear action'),
  ),
});

The descriptions tell the model what the fields mean. s.Infer<typeof workbenchPlanSchema> derives the TypeScript response type from this same schema.

The array is streaming, while its step strings are ordinary strings. Resolved items can appear before the response ends. The Streaming lesson also marks the title as a streaming string, so we can compare the boundaries.

Inspect the contract and the preview

The Schema view shows the response contract selected for this lesson. Beside it, Preview validation keeps the parsed preview, application-rule check, and response status separate. A preview can match the plan rules while the response is still in progress; Accept plan remains disabled.

Send the first prompt, review the completed plan, and accept it. Then send the follow-up. The response uses the same contract while the data changes. A schema describes fields; it does not grant permission to change application state.

Keep the complete example close

The checked React client selects the lesson schema and passes it to useStructuredChat(). Structured output explains the full request and explicit acceptance flow.

Choose the fields your interface needs

ValueSkillet example
Texts.string('The plan title')
Whole numbers.integer('The retry count')
Number with fractionss.number('The measured weight')
True or falses.boolean('Whether the item is checked')
One fixed values.literal('Draft')
One of several stringss.enumeration('Priority', ['low', 'high'])
Named fieldss.object('A plan', { name: s.string('Name') })
Repeated valuess.array('Tags', s.string('One tag'))

An object describes the fields in its shape. If the interface supports different response kinds, give each kind an explicit literal tag:

const responseSchema = s.anyOf([
  s.object('A draft', {
    type: s.literal('Draft'),
    name: s.string('The proposed name'),
  }),
  s.object('A clarification request', {
    type: s.literal('Clarification'),
    question: s.string('What we need to ask the user'),
  }),
]);

Your interface can branch on type. This is useful when the model should ask for missing information instead of inventing a required value. Use s.nullish() as a branch when your contract explicitly permits a nullish value.

Add constraints deliberately

const quantity = s.integer('The retry count', { minimum: 1, maximum: 20 });
const tags = s.array('Tags', s.string('One tag'), { minItems: 1, maxItems: 5 });
const username = s.string('A handle', { pattern: '^@[a-z0-9_]+$' });

Numeric constraints include bounds and multipleOf. Arrays support minItems and maxItems. Strings support pattern and supported format hints. A string's format is forwarded to the provider but is not validated by Hashbrown on the client; validate it in your application when required.

Keep application checks close to the action they protect. The workbench separately validates a nonempty title of at most 120 characters and exactly two nonempty steps of at most 200 characters before Accept plan. That makes the difference between the model's response contract and the application's accepted values visible. A schema does not establish ownership, permission, inventory availability, or successful completion of a run.

Use another schema library

Hashbrown also accepts supported Standard JSON Schema objects through the ~standard specification. It normalizes those schemas to Skillet for parsing and validation. Unsupported keywords throw; a schema accepted by another library may use constructs outside that subset.

Use s.InferSchemaInput<T> and s.InferSchemaOutput<T> when a reusable type must support both Skillet and Standard JSON Schema. For explicit conversion, use s.fromStandardJsonSchema(schema, { mode: 'input' }) or output mode as appropriate. See the Skillet reference for supported constructs and signatures.

Next steps

Use Structured output to generate and review the two-step plan with this schema. Use Exposing components to validate generated component props, or Tool calling to describe arguments for an application function.

Skillet schemasDescribe an application contractExplore it in the workbenchRun the workbenchDescribe the planInspect the contract and the previewKeep the complete example closeChoose the fields your interface needsAdd constraints deliberatelyUse another schema libraryNext steps