Skip to content

Conversation

@karpetrosyan
Copy link
Contributor

@karpetrosyan karpetrosyan commented Oct 1, 2025

  • I understand that this repository is auto-generated and my pull request may not be merged

Changes being requested

Additional context & links

@karpetrosyan karpetrosyan requested a review from a team as a code owner October 1, 2025 05:26
@karpetrosyan karpetrosyan marked this pull request as draft October 1, 2025 05:26
@karpetrosyan karpetrosyan force-pushed the support-zod-v4-schemas branch from c482776 to 8f56474 Compare October 1, 2025 05:27
});

it('throws error on optional fields', () => {
(version === 'v4' ? it.skip : it)('throws error on optional fields', () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as we transform the schema, we actually add missing fields in required property, as we do in python

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm I don't think this is quite right, in Python it's fine because the property being omitted or explicitly set to null results in the same type, None, but in TS it's different.

so if we add properties that are .optional() to the required array, then the API will send them back as null, which breaks the type promise because it'd be typed as property?: string instead of property?: string | null or property: string | null.

the equivalent behaviour here for python would be to only add properties to required when they're both .optional() and .nullable() which is why we throw the current error.

@karpetrosyan karpetrosyan marked this pull request as ready for review October 2, 2025 08:24
Comment on lines 5 to 8
describe.each([
{ version: 'v3', z: zv3 as any },
{ version: 'v4', z: zv4 as any },
])('zodResponseFormat (Zod $version)', ({ version, z }) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👌 this is a cool way to test things

});

it('throws error on optional fields', () => {
(version === 'v4' ? it.skip : it)('throws error on optional fields', () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm I don't think this is quite right, in Python it's fine because the property being omitted or explicitly set to null results in the same type, None, but in TS it's different.

so if we add properties that are .optional() to the required array, then the API will send them back as null, which breaks the type promise because it'd be typed as property?: string instead of property?: string | null or property: string | null.

the equivalent behaviour here for python would be to only add properties to required when they're both .optional() and .nullable() which is why we throw the current error.

@RobertCraigie RobertCraigie mentioned this pull request Oct 3, 2025
1 task
@loky-lp
Copy link

loky-lp commented Oct 20, 2025

Hi, whats blocking this?
Would like to help in any way, the missing support for v4 and v4 mini is a huge pain for us rn.

Copy link
Contributor

@dtmeadows dtmeadows left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 quick comments.

Thanks so much for this @karpetrosyan !

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'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@karpetrosyan any chance you could run all these examples with v3 on this PR just to make sure things are back-compatible?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah thats a good callout, I think I asked to switch them to v4 🤦

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked all of them locally, all worked with v3!

function zodV4ToJsonSchema(schema: z4.ZodType): Record<string, unknown> {
return toStrictJsonSchema(
z4.toJSONSchema(schema, {
target: 'draft-7',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just curious how we picked this value?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've lost the context here a little bit, but looking at it again, it seems like we shouldn't set the concrete target. I'll remove it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I think I did that because the current vendored version that parses Zod schema to JSON Schema is using draft-07 by default

return ensureStrictJsonSchema(schema, [], schema);
}

function isNullable(schema: JSONSchemaDefinition): boolean {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should check schema.nullable as well right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, maybe? it's not part of the json schema spec tho

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wait what, is that openapi only?

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'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah thats a good callout, I think I asked to switch them to v4 🤦

karpetrosyan and others added 3 commits October 24, 2025 15:33
Co-authored-by: Robert Craigie <robert@craigie.dev>
// Handle arrays
const items = jsonSchema.items;
if (isDict(items)) {
// @ts-ignore(2345)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fyi that you should basically never use ts-ignore, instead you should use ts-expect-error so that you're notified if the ignore comment is redundant

Copy link
Collaborator

@RobertCraigie RobertCraigie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! FYI I pushed a couple small commits improving the internal types and with some small cleanup

@RobertCraigie RobertCraigie merged commit 10ef7ff into openai:next Oct 24, 2025
4 of 5 checks passed
@stainless-app stainless-app bot mentioned this pull request Oct 24, 2025
stainless-app bot pushed a commit that referenced this pull request Oct 24, 2025
Co-authored-by: Robert Craigie <robert@craigie.dev>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants