Skip to content

Validate a submit_solution answer against its declared return type - #756

Open
datvo06 wants to merge 2 commits into
eb-doctest-synthesisfrom
dn-validate-final-tool-return
Open

Validate a submit_solution answer against its declared return type#756
datvo06 wants to merge 2 commits into
eb-doctest-synthesisfrom
dn-validate-final-tool-return

Conversation

@datvo06

@datvo06 datvo06 commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

In #694, a Template's return-type validator only runs when the model answers with a direct final message. When it answers via the submit_solution final tool, the answer is returned unvalidated

This PR runs the declared return type's value-validators on a submit_solution answer.

A Template answered by the direct final message has its result validated
through Encodable[return_type], so a return type carrying an AfterValidator
(a compile gate, a probe) is enforced. A Template answered via
submit_solution did not: it returned implementation(*args, **kwargs)
unchecked, so the same validator was silently skipped depending only on how
the model chose to reply.

Run the declared return type's value-validators on the submitted answer.
The answer is an already-built value, not the source the encoding's decode
expects, so apply only the AfterValidators the Encodable annotation carries
-- letting pydantic dispatch (value)/(value, info) and thread the decode
context (the Template's lexical scope plus its arguments, as the direct path
builds it). Return types without a value-validator are unaffected.
@datvo06
datvo06 requested a review from eb8680 August 13, 2026 17:28
Extracting only AfterValidators was incomplete: it skipped types with no
Annotated metadata entirely (int, BaseModel, list), and Before/Wrap
validators and nested element validators never ran. The complete check is
pydantic's own: validate the answer through the full evaluated encoding.

The exception is a decode-style encoding, one carrying a PlainValidator
anywhere (a synthesized callable's source, an image's data URL): its core
validation is a decode from the wire form, so an already-built value cannot
go through it (demonstrated: a built callable raises AttributeError on
module_code, a built PIL image fails validation). For those, run only the
top-level post-decode AfterValidators, which is the post-decode set by
construction. str returns and missing annotations are skipped, matching the
direct path's special case.

Known parity gap, upstream and shared by both paths: TypeToPydanticType
wraps an Annotated return's metadata in a tuple (Annotated[int,
(AfterValidator(...),)]), so pydantic ignores user-authored constraints on
the direct path and here alike; that is a separate fix.
@datvo06
datvo06 force-pushed the dn-validate-final-tool-return branch from b2a793c to 50c6bdb Compare August 13, 2026 19:32
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.

1 participant