Schema

Defining structured I/O

AXAR uses structured input and output to provide a consistent and reliable way to work with agents and tools. It adopts a decorator-based approach that works seamlessly with TypeScript's strong typing. AXAR allows us to define object schema with decorators for metadata, validation, and property descriptions. Alternatively, AXAR also supports Zod, offering a flexible option for schema definitions alongside the decorator-based approach.

Where schemas are needed

Schemas are necessary in the following contexts:

Agent input and output

Schemas define the format of data that an agent receives and returns. For example, in the code below, the GreetingAgent takes input of type GreetingAgentRequest and produces output of type GreetingAgentResponse. We must define schema definitions for both.

@input(GreetingAgentRequest)
@output(GreetingAgentResponse)
export class GreetingAgent extends Agent<
  GreetingAgentRequest,
  GreetingAgentResponse
> {}

If an agent uses simple strings for inputs or outputs, schema definitions are not required.

Tool parameters

Schemas describe the format of objects passed as parameters to tools. In the example below, the tool getWeatherInfo() accepts a parameter of type WeatherParams. A schema definition is required for this type.

@tool('Get weather info')
getWeatherInfo(weather: WeatherParams): string {
  return `The weather is rainy today in ${weather.location}.`;
}

Tools can only accept objects as parameters, and these objects must have a defined schema.

Defining a schema

Schemas are defined using TypeScript classes annotated with @schema(). Each property of that class must have a @property() decorator that describes its purpose.

  • Required properties: Use ! after the property name.

  • Optional properties: Use ? and include the @optional() decorator.

Example: Complete schema definition

@schema()
export class SupportResponse {
  @property('Human-readable advice to give to the customer.')
  support_advice!: string;

  @property("Indicates whether the customer's card should be blocked.")
  block_card!: boolean;

  @property('Risk level of the query (0 to 1).')
  @min(0)
  @max(1)
  risk!: number;

  @property("Customer's emotional state.")
  @enumValues(['Happy', 'Sad', 'Neutral'])
  @optional()
  status?: 'Happy' | 'Sad' | 'Neutral';
}

Special annotations for enums and arrays

Defining array items

The @arrayItems() decorator specifies the schema for array elements.

@schema()
class PostList {
  // 'Post' is a previously defined @schema.
  @arrayItems(() => Post)
  items: Post[];
}

Defining enum properties

The @enumValues() decorator defines valid values for an enum property.

@schema()
class User {
  @enumValues(['admin', 'user', 'guest'])
  role: 'admin' | 'user' | 'guest';
}

Validation decorators

AXAR includes a set of validation decorators to apply constraints on schema properties:

  • Range: @min(value), @max(value)

  • Optional: @optional()

  • Uniqueness: @uniqueItems() (for arrays)

  • Pattern matching: @pattern(regex)

  • Common formats: @email(), @url(), @uuid(), @datetime(), etc.

Using Zod with AXAR

Zod is a popular TypeScript library for runtime type validation and schema definitions. AXAR supports using Zod schemas alongside its decorator-based schema definition approach.

Defining Agent Input and Output with Zod

We can define agent input and output schemas using Zod. Here's an example:

import { model, systemPrompt, Agent, output, input } from '@axarai/axar';
import { z } from 'zod';

// Define input schema
const GreetingAgentRequestSchema = z.object({
  userName: z.string().describe('User name'),
  userMood: z.enum(['happy', 'neutral', 'sad']).describe('User mood'),
  dayOfWeek: z.string().describe('Day of the week'),
  language: z.string().describe('User language preference'),
});

// Define output schema
const GreetingAgentResponseSchema = z.object({
  greeting: z.string().describe('Greeting message to cater to the user mood'),
  moodResponse: z.string().describe('Line acknowledging the user mood'),
  weekendMessage: z
    .string()
    .describe('Personalized message if it is the weekend'),
});

// Infer TypeScript types from schemas
type GreetingAgentRequest = z.infer<typeof GreetingAgentRequestSchema>;
type GreetingAgentResponse = z.infer<typeof GreetingAgentResponseSchema>;

// Create the agent
@model('openai:gpt-4o-mini')
@systemPrompt(
  `Greet the user by their name in a friendly tone in their preferred language.`,
)
@input(GreetingAgentRequestSchema)
@output(GreetingAgentResponseSchema)
export class GreetingAgent extends Agent<
  GreetingAgentRequest,
  GreetingAgentResponse
> {}

// Instantiate and run the agent
(async () => {
  const response = await new GreetingAgent().run({
    userName: 'Alice',
    userMood: 'happy',
    dayOfWeek: 'Saturday',
    language: 'English',
  });
  console.log(response);
})();

The @input and @output annotations accept both @schema-based definitions and ZodSchema.

Using Zod with tools

Zod can also define the schema for tool parameters. Here's an example:

import { model, systemPrompt, Agent, tool } from '@axarai/axar';
import { z } from 'zod';

// Define tool parameter schema
const WeatherParamsSchema = z.object({
  location: z.string().describe('Location of the user'),
});

// Infer TypeScript type from schema
type WeatherParams = z.infer<typeof WeatherParamsSchema>;

// Create the agent
@model('openai:gpt-4o-mini')
@systemPrompt(`
  Greet the user based on the current time and weather.
  Get the current time and weather if you need it.
`)
export class GreetingAgent extends Agent<string, string> {
  // Define a tool for current time
  @tool('Get current time')
  getCurrentTime(): string {
    return `The current time is ${new Date().toLocaleString()}.`;
  }

  // Define a tool with a Zod schema for parameters
  @tool('Get weather info', WeatherParamsSchema)
  getWeatherInfo(weather: WeatherParams): string {
    return `The weather is rainy today in ${weather.location}.`;
  }
}

// Instantiate and run the agent
(async () => {
  const response = await new GreetingAgent().run(
    'Hello, my name is Alice. I am from San Francisco.',
  );
  console.log(response);
})();

When using ZodSchemas with tools, we need to specify the schema as the second argument in the @tool decorator.

Last updated