Skip to content

Commit 9010d99

Browse files
authored
Merge pull request #612 from objectstack-ai/copilot/update-protocol-optimization-report
2 parents 746bcd3 + ee57e3c commit 9010d99

25 files changed

Lines changed: 2938 additions & 257 deletions

PROTOCOL_OPTIMIZATION_REPORT.md

Lines changed: 279 additions & 253 deletions
Large diffs are not rendered by default.

packages/spec/src/ai/agent.test.ts

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import {
44
AIModelConfigSchema,
55
AIToolSchema,
66
AIKnowledgeSchema,
7+
StructuredOutputFormatSchema,
8+
StructuredOutputConfigSchema,
79
type Agent,
810
} from './agent.zod';
911

@@ -569,4 +571,117 @@ Be precise, data-driven, and clear in your explanations.`,
569571
expect(agent.guardrails?.blockedTopics).toContain('financial_advice');
570572
});
571573
});
574+
575+
describe('Structured Output', () => {
576+
it('should accept agent with structuredOutput', () => {
577+
const agent = AgentSchema.parse({
578+
name: 'json_agent',
579+
label: 'JSON Agent',
580+
role: 'Data Formatter',
581+
instructions: 'Always return JSON.',
582+
structuredOutput: {
583+
format: 'json_object',
584+
},
585+
});
586+
587+
expect(agent.structuredOutput?.format).toBe('json_object');
588+
expect(agent.structuredOutput?.strict).toBe(false);
589+
expect(agent.structuredOutput?.retryOnValidationFailure).toBe(true);
590+
expect(agent.structuredOutput?.maxRetries).toBe(3);
591+
});
592+
593+
it('should accept agent with full structuredOutput config', () => {
594+
const agent = AgentSchema.parse({
595+
name: 'strict_agent',
596+
label: 'Strict Agent',
597+
role: 'Validator',
598+
instructions: 'Return strict JSON.',
599+
structuredOutput: {
600+
format: 'json_schema',
601+
schema: { type: 'object', properties: { name: { type: 'string' } } },
602+
strict: true,
603+
retryOnValidationFailure: false,
604+
maxRetries: 5,
605+
fallbackFormat: 'json_object',
606+
transformPipeline: ['trim', 'parse_json', 'validate'],
607+
},
608+
});
609+
610+
expect(agent.structuredOutput?.strict).toBe(true);
611+
expect(agent.structuredOutput?.fallbackFormat).toBe('json_object');
612+
expect(agent.structuredOutput?.transformPipeline).toHaveLength(3);
613+
});
614+
});
615+
});
616+
617+
// ==========================================
618+
// Structured Output Schema Tests
619+
// ==========================================
620+
621+
describe('StructuredOutputFormatSchema', () => {
622+
it('should accept all output formats', () => {
623+
const formats = ['json_object', 'json_schema', 'regex', 'grammar', 'xml'] as const;
624+
formats.forEach(format => {
625+
expect(StructuredOutputFormatSchema.parse(format)).toBe(format);
626+
});
627+
});
628+
629+
it('should reject invalid format', () => {
630+
expect(() => StructuredOutputFormatSchema.parse('yaml')).toThrow();
631+
});
632+
});
633+
634+
describe('StructuredOutputConfigSchema', () => {
635+
it('should accept minimal config', () => {
636+
const config = StructuredOutputConfigSchema.parse({
637+
format: 'json_object',
638+
});
639+
640+
expect(config.format).toBe('json_object');
641+
expect(config.strict).toBe(false);
642+
expect(config.retryOnValidationFailure).toBe(true);
643+
expect(config.maxRetries).toBe(3);
644+
});
645+
646+
it('should accept config with schema', () => {
647+
const config = StructuredOutputConfigSchema.parse({
648+
format: 'json_schema',
649+
schema: {
650+
type: 'object',
651+
properties: {
652+
result: { type: 'string' },
653+
confidence: { type: 'number' },
654+
},
655+
required: ['result'],
656+
},
657+
});
658+
659+
expect(config.schema).toBeDefined();
660+
expect(config.schema?.type).toBe('object');
661+
});
662+
663+
it('should accept config with transform pipeline', () => {
664+
const config = StructuredOutputConfigSchema.parse({
665+
format: 'json_object',
666+
transformPipeline: ['trim', 'parse_json', 'validate', 'coerce_types'],
667+
});
668+
669+
expect(config.transformPipeline).toHaveLength(4);
670+
});
671+
672+
it('should enforce maxRetries min constraint', () => {
673+
expect(() => StructuredOutputConfigSchema.parse({
674+
format: 'json_object',
675+
maxRetries: -1,
676+
})).toThrow();
677+
});
678+
679+
it('should accept fallbackFormat', () => {
680+
const config = StructuredOutputConfigSchema.parse({
681+
format: 'regex',
682+
fallbackFormat: 'json_object',
683+
});
684+
685+
expect(config.fallbackFormat).toBe('json_object');
686+
});
572687
});

packages/spec/src/ai/agent.zod.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,60 @@ export const AIKnowledgeSchema = z.object({
3333
indexes: z.array(z.string()).describe('Vector Store Indexes'),
3434
});
3535

36+
/**
37+
* Structured Output Format
38+
* Defines the expected output format for agent responses
39+
*/
40+
export const StructuredOutputFormatSchema = z.enum([
41+
'json_object',
42+
'json_schema',
43+
'regex',
44+
'grammar',
45+
'xml',
46+
]).describe('Output format for structured agent responses');
47+
48+
/**
49+
* Transform Pipeline Step
50+
* Post-processing steps applied to structured output
51+
*/
52+
export const TransformPipelineStepSchema = z.enum([
53+
'trim',
54+
'parse_json',
55+
'validate',
56+
'coerce_types',
57+
]).describe('Post-processing step for structured output');
58+
59+
/**
60+
* Structured Output Configuration
61+
* Controls how the agent formats and validates its output
62+
*/
63+
export const StructuredOutputConfigSchema = z.object({
64+
/** Output format type */
65+
format: StructuredOutputFormatSchema.describe('Expected output format'),
66+
67+
/** JSON Schema definition for output validation */
68+
schema: z.record(z.string(), z.unknown()).optional().describe('JSON Schema definition for output'),
69+
70+
/** Whether to enforce exact schema compliance */
71+
strict: z.boolean().default(false).describe('Enforce exact schema compliance'),
72+
73+
/** Retry on validation failure */
74+
retryOnValidationFailure: z.boolean().default(true).describe('Retry generation when output fails validation'),
75+
76+
/** Maximum retry attempts */
77+
maxRetries: z.number().int().min(0).default(3).describe('Maximum retries on validation failure'),
78+
79+
/** Fallback format if primary format fails */
80+
fallbackFormat: StructuredOutputFormatSchema.optional().describe('Fallback format if primary format fails'),
81+
82+
/** Post-processing pipeline steps */
83+
transformPipeline: z.array(TransformPipelineStepSchema).optional().describe('Post-processing steps applied to output'),
84+
}).describe('Structured output configuration for agent responses');
85+
86+
export type StructuredOutputFormat = z.infer<typeof StructuredOutputFormatSchema>;
87+
export type TransformPipelineStep = z.infer<typeof TransformPipelineStepSchema>;
88+
export type StructuredOutputConfig = z.infer<typeof StructuredOutputConfigSchema>;
89+
3690
/**
3791
* AI Agent Schema
3892
* Definition of an autonomous agent specialized for a domain.
@@ -132,6 +186,9 @@ export const AgentSchema = z.object({
132186
/** Topics or actions the agent must avoid */
133187
blockedTopics: z.array(z.string()).optional().describe('Forbidden topics or action names'),
134188
}).optional().describe('Safety guardrails for the agent'),
189+
190+
/** Structured Output */
191+
structuredOutput: StructuredOutputConfigSchema.optional().describe('Structured output format and validation configuration'),
135192
});
136193

137194
export type Agent = z.infer<typeof AgentSchema>;

0 commit comments

Comments
 (0)