Skip to Content
DocumentationFeaturesAI Structured Output

AI Structured Output

Use LlmGenerateObjectTool from @loopstack/llm-provider-module to generate structured data conforming to a JSON Schema. Provider-agnostic — works with Claude, OpenAI, and other providers.

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 { z, toJSONSchema } from 'zod'; import { BaseWorkflow, DocumentEntity, Final, Initial, InjectTool, Transition, Workflow } from '@loopstack/common'; import type { LlmGenerateObjectResult } from '@loopstack/llm-provider-module'; import { LlmGenerateObjectTool, LlmMessageDocument } from '@loopstack/llm-provider-module'; import { FileDocument, FileDocumentSchema, FileDocumentType } from './documents/file-document'; @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({ provider: 'claude', model: 'claude-sonnet-4-6' }) llmGenerateObject: LlmGenerateObjectTool; llmResult?: DocumentEntity<FileDocumentType>; @Transition({ from: 'ready', to: 'prompt_executed' }) async prompt() { const args = this.ctx.run.args as { language: string }; const result = await this.llmGenerateObject.call({ outputSchema: toJSONSchema(FileDocumentSchema) as Record<string, unknown>, prompt: this.render(__dirname + '/templates/prompt.md', { language: args.language }), }); const objectResult = result.data as LlmGenerateObjectResult; this.llmResult = await this.repository.save( FileDocument, objectResult.data as FileDocumentType, { validate: 'skip' }, ); } @Final({ from: 'prompt_executed' }) async respond() { await this.repository.save( LlmMessageDocument, { role: 'assistant', content: [{ type: 'text', text: `Generated: ${this.llmResult?.content?.description ?? ''}` }], }, { id: 'status' }, ); } }

How It Works

  1. Convert your Zod schema to JSON Schema using toJSONSchema()
  2. Pass the JSON Schema as outputSchema to llmGenerateObject.call()
  3. The provider forces the LLM to return data matching the schema
  4. Save the result as a typed document using this.repository.save()

Key Parameters

await this.llmGenerateObject.call({ outputSchema: toJSONSchema(MyDocumentSchema) as Record<string, unknown>, prompt: 'Generate structured data.', // provider and model come from @InjectTool defaults });

Registry References

Last updated on