Skip to content

fix(types): distinguish parse-only Zod function tools - #2517

Merged
HAYDEN-OAI merged 1 commit into
mainfrom
dev/hayden/autoimprove-zod-runnable-types
Sep 3, 2026
Merged

HAYDEN-OAI merged 1 commit into
mainfrom
dev/hayden/autoimprove-zod-runnable-types

Conversation

@HAYDEN-OAI

Copy link
Copy Markdown
Contributor

Problem

zodFunction() currently marks every returned tool as runnable, even when its optional function callback is omitted:

const tool = zodFunction({
  name: 'lookup',
  parameters: z.object({ id: z.string() }),
});

client.chat.completions.runTools({
  model: 'gpt-4o',
  messages: [],
  tools: [tool],
});

This compiles on main, but both streaming and non-streaming runners reject before making a request because the tool has no associated function. The existing standardFunction() helper already distinguishes callback-free tools.

Changes

  • Give zodFunction() two overloads: a guaranteed callback produces a runnable tool; an omitted, undefined, or potentially undefined callback produces a parse-only tool.
  • Preserve the existing argument/callback metadata and contextual argument inference, including calls with an explicit schema type argument.
  • Remove the now-unnecessary @ts-expect-error from the implementation.
  • Add compile-time regressions in the existing Zod test file for both runTools() modes, parse/stream acceptance, Zod v3/v4/Mini schemas, explicit generic calls, and promise-returning callbacks.

This is a typing-only correction in two handwritten files. The function body, generated schemas, parser behavior, callback execution, and emitted runtime JavaScript are unchanged. Both files are absent from the pinned generated snapshot; no generated or vendored parser code is modified, and the open Zod schema-conversion PRs are unaffected.

Compatibility

Callback-bearing calls remain runnable. Callback-free tools remain valid for parse() and stream(); they now fail type-checking when passed to runTools(), matching its existing runtime rejection. If a callback's type includes undefined, narrow it before creating a tool intended for execution.

Validation

On Node.js 24.20.0 with pinned pnpm 11.5.1:

  • Final compile-time regressions against main: six expected diagnostics, including unused rejection assertions because invalid runTools() calls were accepted.
  • pnpm exec tsc: passes with the fix.
  • Focused Zod, Standard Schema, and tool-runner tests: 165 passed.
  • pnpm lint and pnpm build: passed.
  • Full CI=true ./scripts/test: 7,009 handwritten and 556 generated tests passed; two existing generated tests skipped.
  • Isolated emitted-declaration fixtures with Zod v3: both CJS and ESM pass TypeScript 4.9.5 and 6.0.3, including negative runTools() assertions.
  • Published source navigation check with TypeScript 4.9.5: passed.
  • Comment-stripped CommonJS and ESM output compared with main: identical.
  • git diff --check: passed.

Adversarial self-review

Reviewed callback presence, explicit undefined, callback unions, contextual and explicit-generic inference, async callbacks, both streaming modes, declaration compatibility, and runtime/security effects. Tightened the callback metadata and compatibility controls, then repeated review and validation. No remaining findings; no new runtime logic or dependencies.

@HAYDEN-OAI
HAYDEN-OAI requested a review from a team as a code owner August 30, 2026 03:11
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 30, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-08-30T03:12:43.530955Z 64cc31d PR opened
🔒 Security Review ✅ Completed 2026-08-30T03:13:24.683552Z 64cc31d PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

Copy link
Copy Markdown
Contributor

Castiron custom code

✅ No new custom-code files detected.

32 mixed files remain; 0 existing customizations changed.

Compared eea2292a4a52 → 64cc31d73a05. Generated baselines verified.

32 existing customizations unchanged
  • api.md
  • scripts/castiron/README.md
  • scripts/castiron/custom_code_report.py
  • scripts/castiron/test_custom_code_report.py
  • src/client.ts
  • src/resources/audio/transcriptions.ts
  • src/resources/audio/translations.ts
  • src/resources/beta/assistants.ts
  • src/resources/beta/beta.ts
  • src/resources/beta/index.ts
  • src/resources/beta/responses/internal-base.ts
  • src/resources/beta/responses/responses.ts
  • src/resources/beta/threads/index.ts
  • src/resources/beta/threads/runs/index.ts
  • src/resources/beta/threads/runs/runs.ts
  • src/resources/beta/threads/threads.ts
  • src/resources/chat/completions/completions.ts
  • src/resources/chat/completions/index.ts
  • src/resources/conversations/index.ts
  • src/resources/embeddings.ts
  • src/resources/files.ts
  • src/resources/fine-tuning/checkpoints/permissions.ts
  • src/resources/images.ts
  • src/resources/responses/internal-base.ts
  • src/resources/responses/responses.ts
  • src/resources/skills/skills.ts
  • src/resources/skills/versions/versions.ts
  • src/resources/vector-stores/file-batches.ts
  • src/resources/vector-stores/files.ts
  • src/resources/webhooks/index.ts
  • src/resources/webhooks/webhooks.ts
  • tests/lib/data-residency.test.ts

A changed generated baseline means this report cannot reliably identify which handwritten lines changed.

Inspect the custom-code diff

Download the exact patch produced by this run (requires repository access):

gh run download 33289705605 --repo openai/openai-node \
  --name castiron-custom-code-33289705605-1 --dir /tmp/castiron-custom-code-33289705605-1
git apply --stat /tmp/castiron-custom-code-33289705605-1/custom-code.patch
cat /tmp/castiron-custom-code-33289705605-1/custom-code.patch

Or reproduce it from an SDK checkout containing the vendored reporter:

git fetch --no-tags origin eea2292a4a523da9405161dde0a79ac5dc2ecb2a 64cc31d73a05821bba744e21553b6561616e4898
python3 scripts/castiron/custom_code_report.py report \
  --base eea2292a4a523da9405161dde0a79ac5dc2ecb2a \
  --head 64cc31d73a05821bba744e21553b6561616e4898 --fetch --require-head-hash --public \
  --out /tmp/castiron-custom-code-64cc31d73a05
cat /tmp/castiron-custom-code-64cc31d73a05/custom-code.patch

This is the current full custom patch for mixed files, not an attribution of only the handwritten lines changed by this PR.

Full report and patch

@openai-sdks

openai-sdks Bot commented Aug 30, 2026 •

Copy link
Copy Markdown
Contributor

OkTest Summary

✅ 237/237 SDK tests passed in 10.511s for Node SDK PR #2517.

Test results — 42 files
Test Result Time
tests/chat-completions-complex-body.test.ts ✅ Passed 175ms
tests/chat-completions-create.test.ts ✅ Passed 201ms
tests/chat-completions-stream.test.ts ✅ Passed 204ms
tests/files-content-binary.test.ts ✅ Passed 160ms
tests/files-create-multipart.test.ts ✅ Passed 146ms
tests/files-list-pagination.test.ts ✅ Passed 151ms
tests/initialize-config.test.ts ✅ Passed 174ms
tests/instance-isolation.test.ts ✅ Passed 131ms
tests/models-list.test.ts ✅ Passed 153ms
tests/responses-background-lifecycle.test.ts ✅ Passed 264ms
tests/responses-body-method-errors.test.ts ✅ Passed 314ms
tests/responses-cancel-timeout.test.ts ✅ Passed 203ms
tests/responses-cancel.test.ts ✅ Passed 259ms
tests/responses-compact-retries.test.ts ✅ Passed 250ms
tests/responses-compact.test.ts ✅ Passed 282ms
tests/responses-create-advanced-stream.test.ts ✅ Passed 129ms
tests/responses-create-advanced.test.ts ✅ Passed 244ms
tests/responses-create-disconnect.test.ts ✅ Passed 1.192s
tests/responses-create-errors.test.ts ✅ Passed 210ms
tests/responses-create-malformed-api-responses.test.ts ✅ Passed 216ms
tests/responses-create-retries.test.ts ✅ Passed 224ms
tests/responses-create-stream-failures.test.ts ✅ Passed 200ms
tests/responses-create-stream-timeout.test.ts ✅ Passed 2.174s
tests/responses-create-stream-wire.test.ts ✅ Passed 2.522s
tests/responses-create-stream.test.ts ✅ Passed 77ms
tests/responses-create-terminal-states.test.ts ✅ Passed 249ms
tests/responses-create-timeout.test.ts ✅ Passed 200ms
tests/responses-create.test.ts ✅ Passed 156ms
tests/responses-delete.test.ts ✅ Passed 215ms
tests/responses-input-items-errors.test.ts ✅ Passed 173ms
tests/responses-input-items-list.test.ts ✅ Passed 188ms
tests/responses-input-items-options.test.ts ✅ Passed 115ms
tests/responses-input-tokens-count-timeout.test.ts ✅ Passed 240ms
tests/responses-input-tokens-count.test.ts ✅ Passed 252ms
tests/responses-malformed-inputs.test.ts ✅ Passed 2.111s
tests/responses-not-found-errors.test.ts ✅ Passed 244ms
tests/responses-parse.test.ts ✅ Passed 195ms
tests/responses-retrieve-retries.test.ts ✅ Passed 283ms
tests/responses-retrieve.test.ts ✅ Passed 238ms
tests/responses-stored-method-errors.test.ts ✅ Passed 554ms
tests/retry-behavior.test.ts ✅ Passed 3.18s
tests/sdk-error-shape.test.ts ✅ Passed 258ms

View OkTest run #33289693940

SDK merge (dac4092c6414) · head (64cc31d73a05) · base (eea2292a4a52) · OkTest (2b1bdfd25e98)

@jbeckwith-oai jbeckwith-oai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed the exact head. The overloads correctly distinguish guaranteed callbacks from parse-only or optional callbacks while retaining inferred Zod argument types. No blocking findings.

@HAYDEN-OAI
HAYDEN-OAI added this pull request to the merge queue Sep 3, 2026
Merged via the queue into main with commit fbd5520 Sep 3, 2026
37 checks passed
@HAYDEN-OAI
HAYDEN-OAI deleted the dev/hayden/autoimprove-zod-runnable-types branch September 3, 2026 20:19
gh-actions-shared Bot pushed a commit to xf-qubit/openai-node that referenced this pull request Sep 3, 2026
## Summary

- Pass the complete options metadata to `AutoParseableTool` and
`AutoParseableResponseTool` instead of passing the parsed argument value
type a second time.
- Preserve explicit parser-output, callback-argument, and function-name
types in both public custom-tool factories.
- Derive Chat Completions runnable metadata from the supplied callback:
a guaranteed callback is runnable, `undefined` is parse-only, and an
optional callback remains unguaranteed. Callback parameters are checked
against the parser's declared output.

## Reproduction

A custom factory call with metadata such as `{ name: 'lookup';
arguments: { city: string }; function: (args: { city: string }) =>
string }` currently returns a tool whose `$parseRaw()` result and name
metadata are `unknown`. The argument object is incorrectly treated as
another metadata object. A valid callback-backed Chat Completions tool
is also incorrectly marked non-runnable and rejected by both public
`runTools()` overloads.

A strict public-import consumer reproduced these errors before the
change. The new compile-time regressions fail on the base and pass with
the fix. They also ensure correcting the metadata cannot accidentally
make a callback-free or optional-callback tool runnable, even if the
declared metadata contains a function or the parsed argument object has
its own `name`, `arguments`, and `function` properties.

## Validation

- Complete `CI=true ./scripts/test`: 7,012 handwritten tests and 556
generated tests pass (2 existing generated skips), with all 12 generated
snapshots passing.
- Focused factory/parser suites: 46 tests pass.
- `pnpm exec tsc --noEmit`, `pnpm lint`, and `pnpm build` pass. CI's
existing type-check step includes the new compile-time assertions.
- Strict emitted-declaration consumer passes TypeScript 4.5.5
(CommonJS), 4.9 (CommonJS/ESM), and 6 (CommonJS/ESM), all with
`skipLibCheck: false`. TypeScript 4.9 also checks distributed source.
- Built CJS/ESM factory checks pass on Node 22.0.0, 24.20.0, and 26.7.0:
12 cases per runtime covering missing/sync/async callbacks, parser and
callback identity, frozen definitions, non-enumerable metadata, and
unchanged JSON serialization.
- Compared both factories' emitted CJS/ESM code with main using the
pinned compilers to normalize comments/formatting; runtime code is
unchanged.

## Adversarial self-review and scope

Reviewed the complete return-type invariant in both factories, not just
the initially failing property access. Verified callback presence is
determined by the actual callback argument rather than unrelated
parser-output properties or a possibly inaccurate metadata declaration.
Covered parse-only and optional tools in both runner modes, sync/async
callbacks, metadata without a function field, string/array/nested parser
outputs, and incompatible callback argument types. Rechecked the final
diff after validation.

This is a types-only change in handwritten code. No runtime parser,
schema conversion, request serialization, generated file, dependency,
custom-code budget, or runtime policy changes. All three paths are
absent from the pinned generated snapshot. Open-PR checks found no
overlap; openai#2517 changes the Zod convenience helper, not these custom
factories.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants