fix(trimmer): keep definition names and instance data in trimmed schemas - #4110
Merged
seratch merged 1 commit intoAug 2, 2026
Merged
Conversation
`_trim_json_schema` drops the prose keywords `description`, `title`, `$comment` and `examples` at every recursion depth. openai#4036 established that `properties` is keyed by user-chosen parameter names rather than by schema keywords, and special-cased it. Two related groups were left uncovered: - Other name-keyed maps: `$defs`, `definitions`, `patternProperties`, `dependentSchemas`, `dependentRequired`. A definition named `description` is deleted while the `$ref` pointing at it survives, so the schema no longer resolves. - Keywords holding instance data rather than subschemas: `default`, `const` and `enum`. A `title` key inside a default value is part of the value, so removing it changes the tool's declared contract. Both are grouped into named frozensets alongside the prose keywords, so the three categories are stated once instead of implied by control flow. This only narrows what gets deleted; genuine subschema keywords such as `items` and `propertyNames` are still trimmed as before.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-up to #4036, which fixed
ToolOutputTrimmerdeleting tool parameters whose names collide with JSON Schema prose keywords and explicitly left the$defscase out of scope. This covers that case and one more of the same kind._trim_json_schemadropsdescription,title,$commentandexamplesat every recursion depth. #4036 recognised thatpropertiesis keyed by user-chosen parameter names, not schema keywords, and special-cased it. Two groups are still walked as if they were subschemas:1. Other name-keyed maps —
$defs,definitions,patternProperties,dependentSchemas,dependentRequired. A definition nameddescriptionis deleted while the$refpointing at it survives, so the schema stops resolving. This is the case #4036 flagged as out of scope, and it is reachable through the SDK's ownfunction_schemapath, not just hand-written schemas — Pydantic keys$defsby class name, and a nested model class nameddescriptionorexamplesproduces exactly this:Through the public filter API (
CallModelData, no network or API key), the trimmed schema is not merely lossy — it fails validation outright:patternPropertiesis affected whenever the pattern is an unanchored bare word ({"title": …}→{}); an anchored^title$happens to survive, which is why this is easy to miss.2. Keywords holding instance data rather than subschemas —
default,const,enum. Nothing inside these is a schema keyword, so atitlekey in a default value is part of the value:The model is then shown a different default than the tool actually has.
enumitems andconstobjects lose the same keys.The three categories are lifted into named module-level frozensets (
_PROSE_SCHEMA_KEYWORDS,_NAME_KEYED_SCHEMA_MAPS,_DATA_SCHEMA_KEYWORDS) so the distinction is stated rather than implied by control flow, and adding a keyword later is a one-line change in one place. This only ever narrows what gets deleted — genuine subschema keywords such asitemsandpropertyNamesare still trimmed exactly as before, and no public API, signature or field order changes.Test plan
Two cases added to
tests/extensions/test_tool_output_trimmer.py, both driven through the public trimmer call rather than the private method:test_keeps_definition_names_and_instance_data_in_schema— every$refstill resolves, all five name-keyed maps keep their keys,default/const/enumare preserved byte for byte, and schema-level and nested prose is still stripped (so the trimming feature stays exercised).test_trims_prose_inside_genuine_subschema_keywords— a guard in the opposite direction:itemsandpropertyNamesmust keep losing their prose, so the fix can't silently grow into a no-op.Verified:
tests/extensions/test_tool_output_trimmer.py: 41 passed with the fix; 1 failed, 40 passed with thesrcchange reverted and the tests kept (fails on the$defsassertion,['Priority'] != ['Priority', 'description']).tests/extensions/(whole directory): 1105 passed, 40 skipped, 11 failed. I diffed the failing-test names against cleanmain(fc084ae2) — the two sets are identical, so there is no regression. They are pre-existing sandbox/session failures (test_bootstrap_persistent_resources_*,test_query_run_loop_*,test_get_items_*,test_runner_with_session_settings_override,test_run_loop_extension_reexports_cloud_bucket_strategy).ruff check: all checks passed.ruff format --check: 2 files already formatted.mypyon the two changed files: the only 2 errors are insandbox/util/tar_utils.py:161andextensions/sandbox/modal/sandbox.py:126; I confirmed both reproduce with my changes stashed, so they are pre-existing and untouched by this PR.Verification-script note:
.agents/skills/code-change-verification/scripts/run.shcould not run here —makeis unavailable on this machine — so I ran the underlying targets directly.Issue number
None — self-reported. This is the follow-up #4036 described as "a one-line follow-up ... if you'd rather have it covered here"; the
default/const/enumhalf was not part of that description.Checks
.agents/skills/code-change-verification/scripts/run.sh— could not:makeunavailable locally (see Test plan); ran the equivalent targets directly insteadmainby diffing failure sets)/reviewbefore submitting this PR — n/aAI assistance was used to draft this change; it was reviewed, reproduced and verified by me.