Skip to content
Merged
532 changes: 279 additions & 253 deletions PROTOCOL_OPTIMIZATION_REPORT.md

Large diffs are not rendered by default.

115 changes: 115 additions & 0 deletions packages/spec/src/ai/agent.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import {
AIModelConfigSchema,
AIToolSchema,
AIKnowledgeSchema,
StructuredOutputFormatSchema,
StructuredOutputConfigSchema,
type Agent,
} from './agent.zod';

Expand Down Expand Up @@ -569,4 +571,117 @@ Be precise, data-driven, and clear in your explanations.`,
expect(agent.guardrails?.blockedTopics).toContain('financial_advice');
});
});

describe('Structured Output', () => {
it('should accept agent with structuredOutput', () => {
const agent = AgentSchema.parse({
name: 'json_agent',
label: 'JSON Agent',
role: 'Data Formatter',
instructions: 'Always return JSON.',
structuredOutput: {
format: 'json_object',
},
});

expect(agent.structuredOutput?.format).toBe('json_object');
expect(agent.structuredOutput?.strict).toBe(false);
expect(agent.structuredOutput?.retryOnValidationFailure).toBe(true);
expect(agent.structuredOutput?.maxRetries).toBe(3);
});

it('should accept agent with full structuredOutput config', () => {
const agent = AgentSchema.parse({
name: 'strict_agent',
label: 'Strict Agent',
role: 'Validator',
instructions: 'Return strict JSON.',
structuredOutput: {
format: 'json_schema',
schema: { type: 'object', properties: { name: { type: 'string' } } },
strict: true,
retryOnValidationFailure: false,
maxRetries: 5,
fallbackFormat: 'json_object',
transformPipeline: ['trim', 'parse_json', 'validate'],
},
});

expect(agent.structuredOutput?.strict).toBe(true);
expect(agent.structuredOutput?.fallbackFormat).toBe('json_object');
expect(agent.structuredOutput?.transformPipeline).toHaveLength(3);
});
});
});

// ==========================================
// Structured Output Schema Tests
// ==========================================

describe('StructuredOutputFormatSchema', () => {
it('should accept all output formats', () => {
const formats = ['json_object', 'json_schema', 'regex', 'grammar', 'xml'] as const;
formats.forEach(format => {
expect(StructuredOutputFormatSchema.parse(format)).toBe(format);
});
});

it('should reject invalid format', () => {
expect(() => StructuredOutputFormatSchema.parse('yaml')).toThrow();
});
});

describe('StructuredOutputConfigSchema', () => {
it('should accept minimal config', () => {
const config = StructuredOutputConfigSchema.parse({
format: 'json_object',
});

expect(config.format).toBe('json_object');
expect(config.strict).toBe(false);
expect(config.retryOnValidationFailure).toBe(true);
expect(config.maxRetries).toBe(3);
});

it('should accept config with schema', () => {
const config = StructuredOutputConfigSchema.parse({
format: 'json_schema',
schema: {
type: 'object',
properties: {
result: { type: 'string' },
confidence: { type: 'number' },
},
required: ['result'],
},
});

expect(config.schema).toBeDefined();
expect(config.schema?.type).toBe('object');
});

it('should accept config with transform pipeline', () => {
const config = StructuredOutputConfigSchema.parse({
format: 'json_object',
transformPipeline: ['trim', 'parse_json', 'validate', 'coerce_types'],
});

expect(config.transformPipeline).toHaveLength(4);
});

it('should enforce maxRetries min constraint', () => {
expect(() => StructuredOutputConfigSchema.parse({
format: 'json_object',
maxRetries: -1,
})).toThrow();
});

it('should accept fallbackFormat', () => {
const config = StructuredOutputConfigSchema.parse({
format: 'regex',
fallbackFormat: 'json_object',
});

expect(config.fallbackFormat).toBe('json_object');
});
});
57 changes: 57 additions & 0 deletions packages/spec/src/ai/agent.zod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,60 @@ export const AIKnowledgeSchema = z.object({
indexes: z.array(z.string()).describe('Vector Store Indexes'),
});

/**
* Structured Output Format
* Defines the expected output format for agent responses
*/
export const StructuredOutputFormatSchema = z.enum([
'json_object',
'json_schema',
'regex',
'grammar',
'xml',
]).describe('Output format for structured agent responses');

/**
* Transform Pipeline Step
* Post-processing steps applied to structured output
*/
export const TransformPipelineStepSchema = z.enum([
'trim',
'parse_json',
'validate',
'coerce_types',
]).describe('Post-processing step for structured output');

/**
* Structured Output Configuration
* Controls how the agent formats and validates its output
*/
export const StructuredOutputConfigSchema = z.object({
/** Output format type */
format: StructuredOutputFormatSchema.describe('Expected output format'),

/** JSON Schema definition for output validation */
schema: z.record(z.string(), z.unknown()).optional().describe('JSON Schema definition for output'),

/** Whether to enforce exact schema compliance */
strict: z.boolean().default(false).describe('Enforce exact schema compliance'),

/** Retry on validation failure */
retryOnValidationFailure: z.boolean().default(true).describe('Retry generation when output fails validation'),

/** Maximum retry attempts */
maxRetries: z.number().int().min(0).default(3).describe('Maximum retries on validation failure'),

/** Fallback format if primary format fails */
fallbackFormat: StructuredOutputFormatSchema.optional().describe('Fallback format if primary format fails'),

/** Post-processing pipeline steps */
transformPipeline: z.array(TransformPipelineStepSchema).optional().describe('Post-processing steps applied to output'),
}).describe('Structured output configuration for agent responses');

export type StructuredOutputFormat = z.infer<typeof StructuredOutputFormatSchema>;
export type TransformPipelineStep = z.infer<typeof TransformPipelineStepSchema>;
export type StructuredOutputConfig = z.infer<typeof StructuredOutputConfigSchema>;

/**
* AI Agent Schema
* Definition of an autonomous agent specialized for a domain.
Expand Down Expand Up @@ -132,6 +186,9 @@ export const AgentSchema = z.object({
/** Topics or actions the agent must avoid */
blockedTopics: z.array(z.string()).optional().describe('Forbidden topics or action names'),
}).optional().describe('Safety guardrails for the agent'),

/** Structured Output */
structuredOutput: StructuredOutputConfigSchema.optional().describe('Structured output format and validation configuration'),
});

export type Agent = z.infer<typeof AgentSchema>;
Expand Down
Loading