Schemas & Zod

Schemas define the type-safe contract between your code and the LLM.

What is a Schema?

A schema is a formal definition of what inputs your LLM program expects and what outputs it should produce. PromptC uses Zod for runtime validation and TypeScript type inference.

import { defineSchema, z } from "@mzhub/promptc";

const MySchema = defineSchema({
  description: "What the LLM should do",
  inputs: {
    // Define input fields with Zod types
  },
  outputs: {
    // Define output fields with Zod types
  }
});

Basic Types

Zod provides all the primitive types you need:

const Example = defineSchema({
  description: "Demonstrate basic types",
  inputs: {
    text: z.string(),
    count: z.number(),
    enabled: z.boolean(),
  },
  outputs: {
    result: z.string(),
    score: z.number(),
    success: z.boolean(),
  }
});

Arrays and Objects

For complex data structures:

const ComplexSchema = defineSchema({
  description: "Handle complex data",
  inputs: {
    items: z.array(z.string()),
    config: z.object({
      temperature: z.number(),
      maxTokens: z.number()
    })
  },
  outputs: {
    entities: z.array(z.object({
      name: z.string(),
      type: z.string(),
      confidence: z.number()
    }))
  }
});

Enums and Literals

Constrain values to specific options:

const SentimentSchema = defineSchema({
  description: "Analyze sentiment of text",
  inputs: {
    text: z.string()
  },
  outputs: {
    sentiment: z.enum(["positive", "negative", "neutral"]),
    confidence: z.number().min(0).max(1)
  }
});

Optional Fields

Mark fields as optional:

const FlexibleSchema = defineSchema({
  description: "Schema with optional fields",
  inputs: {
    text: z.string(),
    language: z.string().optional(),  // Optional input
    context: z.string().default("")   // Default value
  },
  outputs: {
    translation: z.string(),
    notes: z.string().optional()  // Optional output
  }
});

Descriptions

Add descriptions to help the LLM understand field meanings:

const DescribedSchema = defineSchema({
  description: "Extract entities from news articles",
  inputs: {
    article: z.string().describe("The full text of the news article"),
    maxEntities: z.number().describe("Maximum number of entities to extract")
  },
  outputs: {
    entities: z.array(z.object({
      name: z.string().describe("The entity name"),
      type: z.enum(["person", "org", "location"]).describe("Entity category")
    }))
  }
});
Best Practice
Always add descriptions to your schema fields. They become part of the prompt and help the LLM understand exactly what you expect.

Schema Methods

The schema object exposes useful methods:

MethodReturnsDescription
getInputKeys()string[]Array of input field names
getOutputKeys()string[]Array of output field names
validateInput(data)ValidatedInputValidates and parses input data
validateOutput(data)ValidatedOutputValidates and parses output data
descriptionstringThe schema description
const schema = defineSchema({
  description: "My schema",
  inputs: { text: z.string() },
  outputs: { result: z.string() }
});

console.log(schema.getInputKeys());  // ["text"]
console.log(schema.getOutputKeys()); // ["result"]
console.log(schema.description);     // "My schema"

// Validation (throws on invalid data)
const valid = schema.validateInput({ text: "hello" });
const output = schema.validateOutput({ result: "world" });