Skip to content

feat: bump jsonschema-pydantic-converter to 0.4.1, add datamodel-code-generator backend - #1055

Open
vldcmp-uipath wants to merge 1 commit into
mainfrom
claude/pc-4965-schema-codegen-backend
Open

feat: bump jsonschema-pydantic-converter to 0.4.1, add datamodel-code-generator backend#1055
vldcmp-uipath wants to merge 1 commit into
mainfrom
claude/pc-4965-schema-codegen-backend

Conversation

@vldcmp-uipath

@vldcmp-uipath vldcmp-uipath commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Two changes, one dependency apart.

1. fix parsing of inline object - fixed by the converter bump

An object written inline rather than under $defs that held a $ref produced a child model which was never completed: the root reported __pydantic_complete__ = True while the child kept a ForwardRef('__Dynamictype_N'). It surfaced as

X is not fully defined; you should define __Dynamictype_N, then call X.model_rebuild()

once LangChain copies the field annotations into a model in another module to build the tool definition — so an agent failed at tool-binding time, not on execution.

jsonschema-pydantic-converter fixed that upstream in 0.4.1. The only thing needed here is the pin:

-    "jsonschema-pydantic-converter>=0.4.0",
+    "jsonschema-pydantic-converter>=0.4.1",

TestInlineObjectWithRef asserts the shape against both backends, so the guarantee cannot regress on either path.

2. A second schema backend behind a feature flag

Independent of the fix above. Schemas are converted to Pydantic models by two interchangeable backends now, selected by the EnableDatamodelCodeGeneratorConverter feature flag:

flag backend
off (default) jsonschema-pydantic-converter
on datamodel-code-generator

create_model and create_output_model keep their signatures, so no caller changes — all 11 call sites here (and the 2 in uipath-agents-python) go through the same façade.

jsonschema_pydantic_converter.py            façade: flag dispatch + create_output_model
├── _legacy_converter.py                    jsonschema-pydantic-converter
├── _datamodel_code_generator_converter.py  datamodel-code-generator
├── _datamodel_code_generator_base.py       shared base model (serialize_by_alias, extra)
└── _schema_refs.py                         $ref helpers, backend-agnostic

The flag is a gradual rollout, not an escape from a broken backend — with 0.4.1 in place both resolve $refs completely. It exists for what the newer one does better:

  • generated types are named after the schema instead of DynamicType_N, and those names reach the language model through $defs
  • every generated class is homed in the conversion's own pseudo-module. The legacy wrapper only re-homes types collected from $defs, so a model built for an inline object keeps the converter's shared module — where repeated class names across schemas can make a qualified-name lookup land on another schema's class
  • property names that are not valid Python identifiers are repaired and carry an alias, so the declared names still go on the wire (the legacy backend emits a field literally named my-field)

Kept equivalent where callers depend on it

  • Original JSON property names on the wire, via serialize_by_alias from a shared base class. additionalProperties: false still merges over that base to forbid extras.
  • __uipath_marker_name__ on types reached through a $ref, byte-compatible with the legacy names because job-attachment discovery looks types up by them (get_json_paths_by_type(model, "__Job_attachment")). The mapping comes from walking the schema alongside the model tree — the generator sanitizes and de-duplicates class names on its own terms, so no name-based mapping is reliable.
  • not, prefixItems and an empty enum have no Pydantic annotation, so the code generator drops them and the field would accept anything. The legacy backend enforces them itself, so those paths are checked against the original subschema with jsonschema rather than letting validation silently lapse.
  • AgentStartupError naming the unresolved type, raised before generation so the message stays the one users already see.

Differences that remain

Measured, not assumed, and pinned in TestBackendDifferences:

legacy 0.4.1 datamodel-code-generator
inline object holding a $ref complete complete
inline model __module__ _type_converters (shared) own pseudo-module
$defs names the model sees DynamicType_0/1 Fields, Project
additionalProperties-only map nested model dict[str, str]
root title Create_Issue Create_Issue

Testing

The whole conversion contract suite runs against both backends — that is what makes this a genuine toggle rather than two divergent paths.

  • 2860 passed, 3 skipped, 0 failed
  • 138 tests across the two converter modules, 66 per backend; the only 6 not parametrized by backend are the ones that switch backends themselves, to compare the two or to assert the flag routing
  • ruff check, ruff format --check, mypy clean (src and the touched test packages)
  • ~7 ms/schema on the code-generator path (≈280 ms for a 40-schema agent startup)

Copilot AI lite review requested due to automatic review settings September 1, 2026 07:29

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR adds a feature-flagged alternative backend for converting JSON Schema into runtime Pydantic models in the ReAct agent stack, keeping the existing create_model/create_output_model façade and contracts stable for all callers.

Changes:

  • Introduces a new datamodel-code-generator-based conversion backend selected by the AgentSchemaCodegenEnabled feature flag.
  • Refactors $ref handling into backend-agnostic helpers and splits legacy vs. codegen implementations into separate modules.
  • Expands the conversion contract test suite to run against both backends and adds scenario tests covering previously problematic shapes (PC-4965) and runtime lookup contracts.
File summaries
File Description
uv.lock Updates lockfile for new dependency and version bumps.
pyproject.toml Adds datamodel-code-generator dependency and raises jsonschema-pydantic-converter minimum version.
tests/agent/react/test_jsonschema_pydantic_converter.py Runs existing contract tests against both backends via an autouse flag fixture.
tests/agent/react/test_jsonschema_pydantic_converter_scenarios.py Adds scenario-based tests covering runtime contracts and backend differences.
tests/agent/react/test_job_attachments.py Switches schema→model creation to the façade (create_model) and widens an assertion to accept backend differences.
src/uipath_langchain/agent/react/jsonschema_pydantic_converter.py Becomes a façade: feature-flag dispatch + exports of shared $ref helpers.
src/uipath_langchain/agent/react/_schema_refs.py New shared $ref resolution + dangling-ref neutralization helpers.
src/uipath_langchain/agent/react/_legacy_converter.py Extracts the previous jsonschema-pydantic-converter implementation into its own module.
src/uipath_langchain/agent/react/_codegen_converter.py New codegen backend with pseudo-module publication, $ref marker tagging, and constraint guarding.
src/uipath_langchain/agent/react/_codegen_base.py Shared base model ensuring serialize_by_alias and default extra behavior.
Review details
  • Files reviewed: 9/10 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread pyproject.toml
vldcmp-uipath added a commit that referenced this pull request Sep 1, 2026
The codegen backend uses jsonschema to enforce `not`, `prefixItems` and an empty
`enum`, which have no Pydantic annotation. It was only available transitively,
and the import sat inside a try/except that logged a warning and returned the
model unguarded -- so an install without it would have silently stopped
validating those fields.

jsonschema is now a declared dependency, imported at module level, and the
degradation path is gone: a missing dependency fails loudly at import instead of
quietly weakening validation. types-jsonschema joins the dev group, alongside
the existing types-protobuf, so the calls are type-checked rather than ignored.

Also drops the module-level loggers from both backends. The only logging call
lives in the facade now, so they were dead.

Raised by review on #1055.

Co-Authored-By: Claude <noreply@anthropic.com>
vldcmp-uipath added a commit that referenced this pull request Sep 1, 2026
SonarCloud failed the quality gate on the codegen backend: three functions over
the cognitive-complexity limit (45, 25 and 22 against 15 allowed) and the
"Invalid schema" error title written out three times.

The complexity all sat in nested recursive walkers that mixed traversal with the
work done at each node. Traversal is now its own generator per walker, so each
visit() decides what to do with one node and delegates finding the next:

- _tag_referenced_models: _child_nodes() yields each subschema with the models it
  describes, and _models_for_property() resolves a property to its models
- _unenforceable_constraints: _child_paths() yields each subschema with the path
  to its values, and _is_unenforceable() answers the per-node question
- _build_constraint_guard: _values_at_path() moves out to the module level

The error title becomes a constant behind an _invalid_schema() helper, which the
three raise sites share.

Also swaps the constraint validator from a classmethod-wrapped lambda to the
plain callable a "before" model validator accepts, which drops an unused
argument and a type: ignore, and requests the dual-backend fixture through
pytestmark instead of autouse, as the analyzer suggested.

No behaviour change: 2830 passed, and the suites still run every test against
both backends (70 legacy / 68 codegen variants).

Raised by review on #1055.

Co-Authored-By: Claude <noreply@anthropic.com>
@vldcmp-uipath
vldcmp-uipath force-pushed the claude/pc-4965-schema-codegen-backend branch 3 times, most recently from f6eed11 to b3c2a39 Compare September 1, 2026 11:32
@vldcmp-uipath vldcmp-uipath changed the title feat: add datamodel-code-generator schema backend behind a feature flag feat: fix PC-4965 via jsonschema-pydantic-converter 0.4.1, add datamodel-code-generator backend Sep 1, 2026
@vldcmp-uipath
vldcmp-uipath force-pushed the claude/pc-4965-schema-codegen-backend branch from b3c2a39 to 6e651d1 Compare September 1, 2026 13:22
@vldcmp-uipath vldcmp-uipath changed the title feat: fix PC-4965 via jsonschema-pydantic-converter 0.4.1, add datamodel-code-generator backend feat: bump jsonschema-pydantic-converter to 0.4.1, add datamodel-code-generator backend Sep 1, 2026

@andreitava-uip andreitava-uip 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 code-generator backend by running both backends over the same schemas and comparing observable behaviour. The 0.4.1 pin is fine — I reproduced the issue on 0.4.0 in a throwaway venv and confirmed it gone on 0.4.1. The pin alone fixes it and needs no code change.

The second backend does not hold the equivalence the description claims. Flipping the flag changes observable behaviour in several ways that are not in TestBackendDifferences, and two of them break tool execution outright. Everything below is flag-on only, so the default path is unaffected and this is not a merge risk today — but it blocks enabling the flag anywhere.

The suite does not catch any of it: tests/agent passes with UIPATH_FEATURE_EnableDatamodelCodeGeneratorConverter=true except TestBackendSelection::test_defaults_to_the_legacy_backend, which asserts the default.

Blocking:

  • non-identifier property names break every tool call
  • format: password sends ********** instead of the value
  • a $defs name matching the root title returns the wrong model
  • customTypePath in a schema imports and executes an arbitrary module

Also worth noting: FeatureFlags.configure_flags({}) merges rather than clears — only reset_flags() restores the default. The schema_backend fixture gets this right; ad-hoc probes are easy to get wrong.

Details inline.

Comment thread src/uipath_langchain/agent/react/_datamodel_code_generator_base.py
)
module_name = f"{_DYNAMIC_MODULE_PREFIX}_gen_{next(_dynamic_module_counter)}"
try:
models = generate_dynamic_models(

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.

Blocking — schema content can make the generated-and-execd source import an arbitrary module.

datamodel-code-generator parses customTypePath (parser/jsonschema.py:857, a first-class aliased field, no opt-in) and x-python-type, and emits them as a real from <module> import <name> into the source dynamic.py execs. The sanitizers here only normalise names and never see these keys; nothing upstream filters schema keys.

Reproduced:

{"title":"T","type":"object",
 "properties":{"a":{"type":"string","customTypePath":"marker_mod.Thing"}}}
# -> marker_mod imported, its top-level code ran

and the type-confusion variant:

{"properties":{"a":{"type":"string","customTypePath":"builtins.object"}}}
# -> annotation becomes `object`, arbitrary_types_allowed flipped to True,
#    model_validate({"a": 12345}) succeeds on a declared string

Legacy is immune — Optional[str], nothing executed.

The value is constrained to a dotted identifier path, so this is "import an arbitrary named module", not token injection. It still runs that module's top-level code in the agent process, and the type confusion is a validation bypass on its own.

Reachability question I could not settle from the repo: mcp_tool.py:263 sets input_schema=tool.inputSchema straight from the remote MCP server and static_args.py:233 calls create_model on it when argument_properties is non-empty. If tenants can connect MCP servers we do not control, this is remote input reaching code execution. Otherwise it is agent-author-to-runtime, which is a lower bar.

Either way: strip or reject customTypePath and x-python-type before calling the generator.

@vldcmp-uipath vldcmp-uipath Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

overall it is a good observation considering the scenario with the MCP, but it should not be marked as blocking. even if this happens, it would include an user-owned module

also the type-confusion path is a very exaggerated, over-engineering scenario

node: dict[str, Any], models: list[type[BaseModel]]
) -> Iterator[tuple[Any, list[type[BaseModel]]]]:
"""Yield each subschema of `node` with the models it describes."""
for combiner in _COMBINERS:

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.

__uipath_marker_name__ cross-contaminates across combiner branches.

Each anyOf/oneOf/allOf branch is yielded with the parent's unnarrowed models list, so name_models tags every model in that list with that branch's $ref name, hasattr-guarded so the first branch wins permanently.

With result: anyOf[Job_attachment, Note] plus a sibling note: $ref Note:

legacy: get_json_paths_by_type(m, "__Job_attachment") -> ['$.result']
this:                                                 -> ['$.result', '$.note']

That false positive is not benign — get_job_attachments then raises AgentRuntimeError(OUTPUT_VALIDATION_ERROR, category=SYSTEM) on valid data, because job_attachments.py treats any non-empty value at an attachment path as an Attachment.

Reverse the branch order and it becomes a false negative instead: with three branches, all classes end up carrying __Other, and a plain att: $ref Job_attachment property is never found, so attachments are silently skipped.

Multi-$ref combiners are the standard shape zod/TypeScript emit for MCP tool schemas, and nothing upstream normalises them.

Narrow the model list per branch, or skip tagging when a branch is a $ref and len(models) > 1 (which is what legacy effectively does for the ambiguous case).

) -> list[tuple[tuple[str, ...], dict[str, Any]]]:
"""Locate subschemas using a keyword the generator cannot express as a type."""
found: list[tuple[tuple[str, ...], dict[str, Any]]] = []
seen: set[int] = set()

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.

Shared $defs entries lose their constraint guard after the first reference.

seen is keyed on id(node) for the whole walk, and a $ref'd $defs node is the same object each time. The second occurrence short-circuits before _is_unenforceable, so no validator is registered for it.

{"properties":{"a":{"$ref":"#/$defs/N"},"b":{"$ref":"#/$defs/N"}},
 "$defs":{"N":{"type":"object","properties":{"n":{"not":{"type":"string"}}}}}}

{"a":{"n":"str"}} rejected on both; {"b":{"n":"str"}} rejected on legacy, accepted here. Same for a $ref reached from array items, and for one Python dict reused in two places.

Sharing a $defs entry across properties is the normal reason $defs exists, so this is the most likely of the guard gaps to be hit.

The memo is genuinely needed for test_recursive_definition. Keying by traversal path does not work — I tried it and it recurses forever, since the path never repeats. A per-branch ancestor set (frozenset of in-progress node ids, not a global set) terminates on both self-recursive and mutually-recursive schemas and fixes this case.

return node.get("enum") == []


def _child_paths(

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.

_child_paths misses keyword positions that _child_nodes covers, so guarded keywords silently lapse there.

This walker enumerates properties, dict-form items and the combiners. _child_nodes (line 250) additionally descends additionalProperties. _unenforceable_constraints uses only this one.

additionalProperties value = {"type":"string","enum":[]}   legacy rejects | here accepted
additionalProperties value with `not`                      legacy rejects | here accepted
additionalProperties value with `prefixItems`              legacy rejects | here accepted
root-level additionalProperties with `not`                 legacy rejects | here accepted
draft-7 tuple form  items: [ {...not...} ]                 legacy rejects | here accepted

Adding additionalProperties here alone is not enough — I checked both naive variants. Without a path segment the guard validates the map itself against the value schema and falsely rejects {"meta": {}}; with a "{}" segment but no change to _values_at_path the path never resolves and the guard silently no-ops. Both functions have to change together, mirroring the existing "[]" handling.

The tuple-items gap needs the same treatment in both walkers.

More generally: two hand-rolled walkers over the same document, with different keyword sets and different memo keys, is why this and the two comments above are three separate bugs. One shared context-carrying traversal would close all three and stop the next keyword from leaking.

) -> Any:
"""Return a callable enforcing constraints the generated types cannot carry."""
validators = [
(path, jsonschema.Draft202012Validator(subschema))

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.

The guard builds validators from detached subschemas and never checks them, so two classes of input escape as raw exceptions.

  1. No check_schema, and enforce only converts validation results. Anything raised while interpreting a malformed subschema propagates raw out of the mode="before" validator — all three of these build fine and blow up at model_validate:
{"a":{"not":"banana"}}                        -> AttributeError: 'str' object has no attribute 'items'
{"a":{"not":{"type":"string","pattern":"["}}}  -> re.error: unterminated character set
{"a":{"not":{"type":"nope"}}}                  -> jsonschema.exceptions.UnknownType

None is a ValidationError, so BaseUiPathStructuredTool._parse_input does not classify it and the job record ends up as ERROR_AttributeError / category UNKNOWN with no tool or schema context — the opposite of the fail-loudly-at-startup policy stated in create_output_model's docstring.

  1. The validator is constructed from the isolated node with no registry rooted at the document, so a $ref nested inside a guarded subschema cannot resolve and raises PointerToNowhere on every validation, valid input included. _unenforceable_constraints resolves a $ref encountered as the node itself but appends nodes containing nested refs verbatim.

Call check_schema at build time and raise AgentStartupError, build the validator against the whole document (or a referencing registry rooted at schema), and wrap iter_errors so any residual jsonschema exception becomes a ValueError.

Separate, same function: enforce returns early when data is not a dict, so a root-level array schema is never guarded at all — {"type":"array","items":{..."not"...}} with [{"n":"str"}] is rejected by legacy and accepted here.


# Prefix for the per-conversion pseudo-modules that let qualified-name lookups
# resolve each schema's generated classes.
_DYNAMIC_MODULE_PREFIX = "jsonschema_pydantic_converter._dynamic"

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.

The prefix and the counter are duplicated from _legacy_converter.py:32,34 — same prefix string, two independent itertools.count() both starting at 0. The Nth module from each backend collides in sys.modules, and the later registration replaces the earlier one:

dmcg#1   -> ..._dynamic_1
legacy#1 -> ..._dynamic_0
legacy#2 -> ..._dynamic_1     # collision
# sys.modules["..._dynamic_1"].T is now legacy#2's class, not dmcg#1's

That is exactly the failure the per-conversion module exists to prevent, per the docstrings in both files.

It needs both backends to run in one process, so production reachability depends on whether flags are ever reconfigured after startup — I could not establish that. It is guaranteed in the test process, where the fixture flips the flag per test.

Move the prefix, counter and _create_dynamic_module into one module both backends import.

Copy link
Copy Markdown
Contributor Author

Thanks — this is a genuinely good review. I re-derived six of the findings from scratch in a local venv rather than take them on trust, and all six reproduced exactly as described. Measured, legacy vs code-generator, flag flipped via FeatureFlags:

finding legacy code-generator
customTypePath: builtins.object Optional[str], {"a": 12345} rejected object | None, {"a": 12345} accepted for a declared string
format: password model_dump(){'pw': 's3cret'} {'pw': SecretStr('**********')}; model_dump_json(){"pw":"**********"}
property first-name / Content-Type getattr-by-alias OK AttributeError: 'R' object has no attribute 'first-name'
root title colliding with a $defs name fields ['r'], required ['r'], {} rejected fields ['x'], required [], {} accepted
format: email Optional[str] AgentStartupError at conversion
shared $defs entry, second reference both rejected a rejected, b accepted

So I'm not going to argue with any of it. Three points I want to concede explicitly rather than leave implied:

The equivalence claim in the description was mine and it was wrong. "The whole conversion contract suite runs against both backends" was true as a statement about parametrization and worthless as evidence — your observation that TestPropertyNaming asserts model_dump() and never invokes a tool is exactly the hole. The suite proved the two backends agree on what I thought to check, which is not the same as agreeing. The "Differences that remain" table is materially incomplete and I'm correcting it.

Non-identifier property names failing is the worst of these, because repairing those names is the reason the backend exists. The headline feature is the broken one.

Your customTypePath reachability question I also can't settle from the repo. mcp_tool.py:263 does pass tool.inputSchema through unfiltered, so if tenants can attach MCP servers we don't control, schema content reaches a from <module> import <name> in execd source. Someone with the tenancy model in view should answer that; either way the filter is warranted, and the type-confusion variant is a validation bypass on its own merits.

On direction. The >=0.4.1 bump is uncontested, you verified it independently, and it needs no code change — but changes_requested blocks it along with everything else. Rather than hold the actual fix behind a backend that needs real design work (one context-carrying traversal replacing the two walkers, a schema-key filter, a decision on the format mapping policy, and the alias-aware _parse_input fix in code we own), I'd propose splitting: land the pin on its own, and iterate on the code-generator backend in a follow-up against your findings.

Tell me if you'd rather it all stayed here and I'll work through the list in this PR instead.


Generated by Claude Code

@vldcmp-uipath
vldcmp-uipath force-pushed the claude/pc-4965-schema-codegen-backend branch 3 times, most recently from faac080 to c3eb04e Compare September 3, 2026 11:24
…-generator backend

Two changes, one dependency apart.

An object written inline rather than under $defs that held a $ref produced a
child model which was never completed -- the root reported complete while the
child kept a ForwardRef -- so an agent failed at tool-binding time rather than
on execution. jsonschema-pydantic-converter fixed that in 0.4.1, so the pin
moves from >=0.4.0 to >=0.4.1. That bump is the whole of that fix.

Separately, schemas become Pydantic models through two interchangeable backends
now, selected by the EnableDatamodelCodeGeneratorConverter feature flag: off
(the default) keeps jsonschema-pydantic-converter, on switches to
datamodel-code-generator. create_model and create_output_model keep their
signatures, so no caller changes. The newer backend names generated types after
the schema, homes every class in the conversion's own pseudo-module, and repairs
property names that are not valid Python identifiers while keeping the declared
names on the wire. The flag is a gradual rollout, not an escape from a broken
backend.

Where callers depend on behaviour the generator does not provide, it is
restored: the original JSON property names on the wire, __uipath_marker_name__
on types reached through a $ref, jsonschema enforcement for not/prefixItems/
empty enum, and an AgentStartupError naming an unresolved type.

Review fix: a ``format`` no longer changes the value it annotates. The generator
retypes on format by default -- password to SecretStr, which serializes as a mask
instead of the credential; email and ulid to types whose validators are not
installed, failing conversion outright; date-time to an aware datetime, rejecting
the naive stamps models emit; and a dozen more to objects json.dumps cannot
serialize. Every (type, format) pair is pinned back to its type's own default,
derived from the generator's table so a format added upstream is covered too.

Review fix: a declared JSON property name now resolves as an attribute on the
generated model. Serializing by alias makes model_dump() alias-keyed, and
LangChain's BaseTool._parse_input reads each dumped key back off the instance
with getattr -- a lookup by JSON name that a sanitized field did not answer, so
any tool whose schema had a property like Content-Type raised AttributeError
mid-call. Handled on the base model so the mismatch is closed for every
alias-unaware consumer, with a test that invokes a real tool.

Review fix: the model handed back is the document's own, not a definition that
won its name. The generator asks for the root's name first but reserves it last,
so a schema titled Root alongside $defs/Root leaves the definition holding Root
and renames the root around it. The requested name is no dependable handle
either way -- names are singularized after the callback returns, and it is
called more than once for some subschemas. The root is identified by structure
instead: it carries the document's own properties, and nothing else refers to
it, with the requested names left to break ties structure cannot settle. Until
now such a tool showed the model the definition's arguments and rejected the
ones its own schema declared, as a prompt mismatch the author could not fix.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ADgPaRAEta3H3toAjM4DXu
@vldcmp-uipath
vldcmp-uipath force-pushed the claude/pc-4965-schema-codegen-backend branch from c3eb04e to 7a8d14e Compare September 3, 2026 12:03
@sonarqubecloud

sonarqubecloud Bot commented Sep 3, 2026

Copy link
Copy Markdown

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.

3 participants