Skip to content
2 changes: 1 addition & 1 deletion examples/parsing-run-tools.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import OpenAI from 'openai';
import z from 'zod/v3';
import z from 'zod/v4'; // Also works for 'zod/v3'
import { zodFunction } from 'openai/helpers/zod';

const Table = z.enum(['orders', 'customers', 'products']);
Expand Down
2 changes: 1 addition & 1 deletion examples/parsing-stream.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { zodResponseFormat } from 'openai/helpers/zod';
import OpenAI from 'openai/index';
import { z } from 'zod/v3';
import { z } from 'zod/v4'; // Also works for 'zod/v3'

const Step = z.object({
explanation: z.string(),
Expand Down
2 changes: 1 addition & 1 deletion examples/parsing-tools-stream.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { zodFunction } from 'openai/helpers/zod';
import OpenAI from 'openai/index';
import { z } from 'zod/v3';
import { z } from 'zod/v4'; // Also works for 'zod/v3'

const GetWeatherArgs = z.object({
city: z.string(),
Expand Down
2 changes: 1 addition & 1 deletion examples/parsing-tools.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { zodFunction } from 'openai/helpers/zod';
import OpenAI from 'openai/index';
import { z } from 'zod/v3';
import { z } from 'zod/v4'; // Also works for 'zod/v3'

const Table = z.enum(['orders', 'customers', 'products']);

Expand Down
2 changes: 1 addition & 1 deletion examples/parsing.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { zodResponseFormat } from 'openai/helpers/zod';
import OpenAI from 'openai/index';
import { z } from 'zod/v3';
import { z } from 'zod/v4'; // Also works for 'zod/v3'

const Step = z.object({
explanation: z.string(),
Expand Down
2 changes: 1 addition & 1 deletion examples/responses/streaming-tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { OpenAI } from 'openai';
import { zodResponsesFunction } from 'openai/helpers/zod';
import { z } from 'zod/v3';
import { z } from 'zod/v4'; // Also works for 'zod/v3'

const Table = z.enum(['orders', 'customers', 'products']);
const Column = z.enum([
Expand Down
2 changes: 1 addition & 1 deletion examples/responses/structured-outputs-tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { OpenAI } from 'openai';
import { zodResponsesFunction } from 'openai/helpers/zod';
import { z } from 'zod/v3';
import { z } from 'zod/v4'; // Also works for 'zod/v3'

const Table = z.enum(['orders', 'customers', 'products']);
const Column = z.enum([
Expand Down
2 changes: 1 addition & 1 deletion examples/responses/structured-outputs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { OpenAI } from 'openai';
import { zodTextFormat } from 'openai/helpers/zod';
import { z } from 'zod/v3';
import { z } from 'zod/v4'; // Also works for 'zod/v3'

const Step = z.object({
explanation: z.string(),
Expand Down
2 changes: 1 addition & 1 deletion examples/tool-call-helpers-zod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import OpenAI from 'openai';
import { zodFunction } from 'openai/helpers/zod';
import { z } from 'zod/v3';
import { z } from 'zod/v4'; // Also works for 'zod/v3'

// gets API Key from environment variable OPENAI_API_KEY
const openai = new OpenAI();
Expand Down
2 changes: 1 addition & 1 deletion examples/ui-generation.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import OpenAI from 'openai';
import { z } from 'zod/v3';
import { z } from 'zod/v4'; // Also works for 'zod/v3'
import { zodResponseFormat } from 'openai/helpers/zod';

const openai = new OpenAI();
Expand Down
112 changes: 94 additions & 18 deletions src/helpers/zod.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ResponseFormatJSONSchema } from '../resources/index';
import type { infer as zodInfer, ZodType } from 'zod/v3';
import { z as z3 } from 'zod/v3';
import { z as z4 } from 'zod/v4';
import {
AutoParseableResponseFormat,
AutoParseableTextFormat,
Expand All @@ -11,8 +12,10 @@ import {
import { zodToJsonSchema as _zodToJsonSchema } from '../_vendor/zod-to-json-schema';
import { AutoParseableResponseTool, makeParseableResponseTool } from '../lib/ResponsesParser';
import { type ResponseFormatTextJSONSchemaConfig } from '../resources/responses/responses';
import { toStrictJsonSchema } from '../lib/transform';
import { JSONSchema } from '../lib/jsonschema';

function zodToJsonSchema(schema: ZodType, options: { name: string }): Record<string, unknown> {
function zodV3ToJsonSchema(schema: z3.ZodType, options: { name: string }): Record<string, unknown> {
return _zodToJsonSchema(schema, {
openaiStrictMode: true,
name: options.name,
Expand All @@ -22,6 +25,18 @@ function zodToJsonSchema(schema: ZodType, options: { name: string }): Record<str
});
}

function zodV4ToJsonSchema(schema: z4.ZodType): Record<string, unknown> {
return toStrictJsonSchema(
z4.toJSONSchema(schema, {
target: 'draft-7',
}) as JSONSchema,
) as Record<string, unknown>;
}

function isZodV4(zodObject: z3.ZodType | z4.ZodType): zodObject is z4.ZodType {
return '_zod' in zodObject;
}

/**
* Creates a chat completion `JSONSchema` response format object from
* the given Zod schema.
Expand Down Expand Up @@ -59,37 +74,57 @@ function zodToJsonSchema(schema: ZodType, options: { name: string }): Record<str
* This can be passed directly to the `.create()` method but will not
* result in any automatic parsing, you'll have to parse the response yourself.
*/
export function zodResponseFormat<ZodInput extends ZodType>(
export function zodResponseFormat<ZodInput extends z3.ZodType>(
zodObject: ZodInput,
name: string,
props?: Omit<ResponseFormatJSONSchema.JSONSchema, 'schema' | 'strict' | 'name'>,
): AutoParseableResponseFormat<z3.infer<ZodInput>>;
export function zodResponseFormat<ZodInput extends z4.ZodType>(
zodObject: ZodInput,
name: string,
props?: Omit<ResponseFormatJSONSchema.JSONSchema, 'schema' | 'strict' | 'name'>,
): AutoParseableResponseFormat<z4.infer<ZodInput>>;
export function zodResponseFormat<ZodInput extends z3.ZodType | z4.ZodType>(
zodObject: ZodInput,
name: string,
props?: Omit<ResponseFormatJSONSchema.JSONSchema, 'schema' | 'strict' | 'name'>,
): AutoParseableResponseFormat<zodInfer<ZodInput>> {
): unknown {
return makeParseableResponseFormat(
{
type: 'json_schema',
json_schema: {
...props,
name,
strict: true,
schema: zodToJsonSchema(zodObject, { name }),
schema: isZodV4(zodObject) ? zodV4ToJsonSchema(zodObject) : zodV3ToJsonSchema(zodObject, { name }),
},
},
(content) => zodObject.parse(JSON.parse(content)),
);
}

export function zodTextFormat<ZodInput extends ZodType>(
export function zodTextFormat<ZodInput extends z3.ZodType>(
zodObject: ZodInput,
name: string,
props?: Omit<ResponseFormatTextJSONSchemaConfig, 'schema' | 'type' | 'strict' | 'name'>,
): AutoParseableTextFormat<zodInfer<ZodInput>> {
): AutoParseableTextFormat<z3.infer<ZodInput>>;
export function zodTextFormat<ZodInput extends z4.ZodType>(
zodObject: ZodInput,
name: string,
props?: Omit<ResponseFormatTextJSONSchemaConfig, 'schema' | 'type' | 'strict' | 'name'>,
): AutoParseableTextFormat<z4.infer<ZodInput>>;
export function zodTextFormat<ZodInput extends z3.ZodType | z4.ZodType>(
zodObject: ZodInput,
name: string,
props?: Omit<ResponseFormatTextJSONSchemaConfig, 'schema' | 'type' | 'strict' | 'name'>,
): unknown {
return makeParseableTextFormat(
{
type: 'json_schema',
...props,
name,
strict: true,
schema: zodToJsonSchema(zodObject, { name }),
schema: isZodV4(zodObject) ? zodV4ToJsonSchema(zodObject) : zodV3ToJsonSchema(zodObject, { name }),
},
(content) => zodObject.parse(JSON.parse(content)),
);
Expand All @@ -100,23 +135,41 @@ export function zodTextFormat<ZodInput extends ZodType>(
* automatically by the chat completion `.runTools()` method or automatically
* parsed by `.parse()` / `.stream()`.
*/
export function zodFunction<Parameters extends ZodType>(options: {
export function zodFunction<Parameters extends z3.ZodType>(options: {
name: string;
parameters: Parameters;
function?: ((args: zodInfer<Parameters>) => unknown | Promise<unknown>) | undefined;
function?: ((args: z3.infer<Parameters>) => unknown | Promise<unknown>) | undefined;
description?: string | undefined;
}): AutoParseableTool<{
arguments: Parameters;
name: string;
function: (args: zodInfer<Parameters>) => unknown;
}> {
// @ts-expect-error TODO
function: (args: z3.infer<Parameters>) => unknown;
}>;
export function zodFunction<Parameters extends z4.ZodType>(options: {
name: string;
parameters: Parameters;
function?: ((args: z4.infer<Parameters>) => unknown | Promise<unknown>) | undefined;
description?: string | undefined;
}): AutoParseableTool<{
arguments: Parameters;
name: string;
function: (args: z4.infer<Parameters>) => unknown;
}>;
export function zodFunction<Parameters extends z3.ZodType | z4.ZodType>(options: {
name: string;
parameters: Parameters;
function?: ((args: any) => unknown | Promise<unknown>) | undefined;
description?: string | undefined;
}): unknown {
return makeParseableTool<any>(
{
type: 'function',
function: {
name: options.name,
parameters: zodToJsonSchema(options.parameters, { name: options.name }),
parameters:
isZodV4(options.parameters) ?
zodV4ToJsonSchema(options.parameters)
: zodV3ToJsonSchema(options.parameters, { name: options.name }),
strict: true,
...(options.description ? { description: options.description } : undefined),
},
Expand All @@ -128,21 +181,44 @@ export function zodFunction<Parameters extends ZodType>(options: {
);
}

export function zodResponsesFunction<Parameters extends ZodType>(options: {
export function zodResponsesFunction<Parameters extends z3.ZodType>(options: {
name: string;
parameters: Parameters;
function?: ((args: z3.infer<Parameters>) => unknown | Promise<unknown>) | undefined;
description?: string | undefined;
}): AutoParseableResponseTool<{
arguments: Parameters;
name: string;
function: (args: z3.infer<Parameters>) => unknown;
}>;
export function zodResponsesFunction<Parameters extends z4.ZodType>(options: {
name: string;
parameters: Parameters;
function?: ((args: z4.infer<Parameters>) => unknown | Promise<unknown>) | undefined;
description?: string | undefined;
}): AutoParseableResponseTool<{
arguments: Parameters;
name: string;
function: (args: z4.infer<Parameters>) => unknown;
}>;
export function zodResponsesFunction<Parameters extends z3.ZodType | z4.ZodType>(options: {
name: string;
parameters: Parameters;
function?: ((args: zodInfer<Parameters>) => unknown | Promise<unknown>) | undefined;
function?: ((args: unknown) => unknown | Promise<unknown>) | undefined;
description?: string | undefined;
}): AutoParseableResponseTool<{
arguments: Parameters;
name: string;
function: (args: zodInfer<Parameters>) => unknown;
function: (args: unknown) => unknown;
}> {
return makeParseableResponseTool<any>(
{
type: 'function',
name: options.name,
parameters: zodToJsonSchema(options.parameters, { name: options.name }),
parameters:
isZodV4(options.parameters) ?
zodV4ToJsonSchema(options.parameters)
: zodV3ToJsonSchema(options.parameters, { name: options.name }),
strict: true,
...(options.description ? { description: options.description } : undefined),
},
Expand Down
24 changes: 24 additions & 0 deletions src/lib/jsonschema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,30 @@ export interface JSONSchema {
oneOf?: JSONSchemaDefinition[] | undefined;
not?: JSONSchemaDefinition | undefined;

/**
* @see https://json-schema.org/draft/2020-12/json-schema-core.html#section-8.2.4
*/
$defs?:
| {
[key: string]: JSONSchemaDefinition;
}
| undefined;

/**
* @deprecated Use $defs instead (draft 2019-09+)
* @see https://tools.ietf.org/doc/html/draft-handrews-json-schema-validation-01#page-22
*/
definitions?:
| {
[key: string]: JSONSchemaDefinition;
}
| undefined;

/**
* @see https://json-schema.org/draft/2020-12/json-schema-core#ref
*/
$ref?: string | undefined;

/**
* @see https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-7
*/
Expand Down
Loading
Loading