Skip to Content
DocumentationFeaturesAI Structured Output

AI Structured Output

Use ClaudeGenerateDocument to have the LLM generate structured data conforming to a Zod schema. The output is validated and stored as a typed document.

Define a Document

import { z } from 'zod'; import { Document } from '@loopstack/common'; export const FileDocumentSchema = z .object({ filename: z.string(), description: z.string(), code: z.string(), }) .strict(); export type FileDocumentType = z.infer<typeof FileDocumentSchema>; @Document({ schema: FileDocumentSchema, uiConfig: __dirname + '/file-document.yaml', }) export class FileDocument { filename: string; description: string; code: string; }

Workflow Example

import { ClaudeGenerateDocument, ClaudeMessageDocument } from '@loopstack/claude-module'; import { BaseWorkflow, DocumentEntity, Final, Initial, InjectTool, Transition, Workflow } from '@loopstack/common'; @Workflow({ uiConfig: __dirname + '/prompt-structured-output.ui.yaml', schema: z.object({ language: z.enum(['python', 'javascript', 'java', 'cpp', 'ruby', 'go', 'php']).default('python'), }), }) export class PromptStructuredOutputWorkflow extends BaseWorkflow { @InjectTool() claudeGenerateDocument: ClaudeGenerateDocument; llmResult?: DocumentEntity<FileDocumentType>; @Initial({ to: 'ready' }) async greeting() { const args = this.ctx.args as { language: string }; await this.repository.save( ClaudeMessageDocument, { role: 'assistant', content: [{ type: 'text', text: `Creating a 'Hello, World!' script in ${args.language}...` }], }, { id: 'status' }, ); } @Transition({ from: 'ready', to: 'prompt_executed' }) async prompt() { const args = this.ctx.args as { language: string }; const result = await this.claudeGenerateDocument.call({ claude: { model: 'claude-sonnet-4-6' }, response: { document: FileDocument }, prompt: this.render(__dirname + '/templates/prompt.md', { language: args.language }), }); this.llmResult = result.data as DocumentEntity<FileDocumentType>; } @Final({ from: 'prompt_executed' }) async respond() { await this.repository.save( ClaudeMessageDocument, { role: 'assistant', content: [{ type: 'text', text: `Successfully generated: ${this.llmResult?.content?.description ?? ''}` }], }, { id: 'status' }, ); } }

How It Works

  1. claudeGenerateDocument calls the LLM with a structured output schema derived from the document’s Zod schema
  2. The LLM response is validated against the schema
  3. The result is returned as a DocumentEntity with typed content
  4. The document is automatically saved and displayed in the UI

Key Parameters

await this.claudeGenerateDocument.call({ claude: { model: 'claude-sonnet-4-6' }, response: { document: FileDocument, // Target document class id: 'unique-id', // Optional: custom document ID }, prompt: 'Generate a Hello World script.', });

Registry References

Last updated on