Structured output for TypeScript LLM apps. Define the expected shape with Zod, and zodstructor turns free-form model output into validated, strongly typed data — with provider-native structured output, function/tool calling, validation repair retries, and transport retries.
Status: beta. The core path is tested and buildable; real-provider compatibility should be gated by the integration matrix before production rollout.
- Schema-first:
z.infer<T>flows from the Zod schema to the resolved value. - Output modes:
auto,json_schema,json_object,tool_call, andprompt. - Strict schema generation: optional/default/catch fields become
anyOf [T, null]in strict object mode. - Two retry layers: validation retries feed Zod issues back to the model; transport retries use exponential backoff for network/HTTP failures.
- Streaming: raw text streaming with final validation, plus best-effort partial previews through
partialSchema. - Providers: OpenAI chat.completions, OpenAI Responses API, OpenAI-compatible gateways, Anthropic, and Google Gemini.
npm install zodstructor zod
# Install only the provider SDKs you use
npm install openai # OpenAI / OpenAI-compatible / Responses
npm install @anthropic-ai/sdk # Anthropic
npm install @google/generative-ai # Google Geminiimport { z } from 'zod'
import { createZodstructor, OpenAIProvider } from 'zodstructor'
const UserSchema = z.object({
name: z.string(),
age: z.number().int().min(0),
email: z.string().email().optional(),
tags: z.array(z.string()).max(5),
})
const client = createZodstructor(new OpenAIProvider({ apiKey: process.env.OPENAI_API_KEY }))
const user = await client.completion({
schema: UserSchema,
model: 'gpt-4o-mini',
mode: 'json_schema',
messages: [
{ role: 'user', content: 'Extract: Ada, 36, ada@example.com, tags: admin, security' },
],
})| Mode | Behavior | Best for |
|---|---|---|
json_schema |
Provider-native schema enforcement where available | Highest guarantee on OpenAI Responses/chat structured output |
tool_call |
OpenAI/Anthropic function or tool calling; arguments are validated as JSON | Tool-native models and Agent workflows |
json_object |
JSON-only response without native schema enforcement | Compatible gateways with JSON mode |
prompt |
Prompt injection only | Legacy or limited OpenAI-compatible endpoints |
auto |
Backward-compatible default; prefer explicit modes in new code | Migration path |
await client.completion({
schema: UserSchema,
messages,
maxRetries: 3, // validation repair attempts
transportRetries: 2, // network/HTTP attempts, independent from validation retries
retryDelayMs: 250, // exponential backoff base
onRetry: (attempt, error) => logValidation(error.issues),
onTransportRetry: (attempt, error) => logTransport(error),
})import { streamStructured } from 'zodstructor'
const stream = streamStructured({
provider,
schema: UserSchema,
partialSchema: UserSchema.partial(),
onPartial: preview => renderPreview(preview),
messages,
})
for await (const chunk of stream) process.stdout.write(chunk)
const { value, rawText } = await stream.return()Partial preview is a best-effort truncated-JSON repair: it closes unterminated strings/brackets and never invents missing fields. Final correctness still comes from the full schema validation.
npm install
npm run check
npm test
npm run build
# Optional real OpenAI integration test
$env:OPENAI_API_KEY="sk-..."
npm testMIT © zuoanCo. See LICENSE.
