Skip to main content

Structured Output

The SDK supports parsing AI responses into strongly-typed data structures using Zod schemas.

Basic Usage

import { Droid } from '@activade/droid-sdk';
import { z } from 'zod';

const droid = new Droid();

const UserSchema = z.object({
id: z.number(),
name: z.string(),
email: z.string().email()
});

const result = await droid.exec('Generate a user object as JSON');
const user = result.parse(UserSchema);

// TypeScript knows the type:
console.log(user.id); // number
console.log(user.name); // string
console.log(user.email); // string

Parse Methods

parse() - Strict Parsing

Throws on invalid data:

const ConfigSchema = z.object({
port: z.number(),
host: z.string()
});

try {
const config = result.parse(ConfigSchema);
console.log(`Server: ${config.host}:${config.port}`);
} catch (error) {
if (error instanceof ParseError) {
console.error('Invalid JSON:', error.message);
}
// Schema validation errors are thrown as-is
}

tryParse() - Safe Parsing

Returns null on failure:

const config = result.tryParse(ConfigSchema);

if (config) {
console.log('Config:', config);
} else {
console.log('Response was not valid config JSON');
}

Complex Schemas

Nested Objects

const ProjectSchema = z.object({
name: z.string(),
version: z.string(),
dependencies: z.record(z.string()),
scripts: z.object({
build: z.string().optional(),
test: z.string().optional()
})
});

const result = await droid.exec('Analyze package.json and return as JSON');
const project = result.parse(ProjectSchema);

Arrays

const TodoSchema = z.object({
id: z.number(),
title: z.string(),
completed: z.boolean()
});

const TodoListSchema = z.array(TodoSchema);

const result = await droid.exec('Generate 5 todo items as JSON array');
const todos = result.parse(TodoListSchema);

Unions and Optionals

const ResponseSchema = z.object({
status: z.enum(['success', 'error']),
data: z.union([
z.object({ users: z.array(z.string()) }),
z.object({ error: z.string() })
]).optional()
});

JSON Schema Output

For direct schema specification in prompts:

const result = await thread.run('Generate user data', {
outputSchema: {
type: 'object',
properties: {
id: { type: 'number' },
name: { type: 'string' }
},
required: ['id', 'name']
}
});

Error Handling

import { ParseError } from '@activade/droid-sdk';
import { z } from 'zod';

try {
const data = result.parse(MySchema);
} catch (error) {
if (error instanceof ParseError) {
// Response was not valid JSON
console.error('JSON parse failed:', error.message);
console.error('Raw response:', error.rawText);
} else if (error instanceof z.ZodError) {
// JSON was valid but didn't match schema
console.error('Validation failed:', error.issues);
}
}

Prompting for JSON

For best results, be explicit about JSON format:

// Good - explicit JSON request
const result = await droid.exec(
'Analyze the codebase and return a JSON object with: ' +
'{ files: number, lines: number, languages: string[] }'
);

// Better - with example
const result = await droid.exec(`
Analyze the codebase and return JSON like:
{
"files": 42,
"lines": 1234,
"languages": ["TypeScript", "JavaScript"]
}
`);

Best Practices

  1. Use tryParse for optional data - When JSON might not be present
  2. Use parse for required data - When you need guaranteed structure
  3. Define precise schemas - Be specific about types and constraints
  4. Handle both error types - ParseError and ZodError
  5. Prompt clearly - Ask explicitly for JSON with structure hints