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
- Use tryParse for optional data - When JSON might not be present
- Use parse for required data - When you need guaranteed structure
- Define precise schemas - Be specific about types and constraints
- Handle both error types - ParseError and ZodError
- Prompt clearly - Ask explicitly for JSON with structure hints