feat: Add typed structured outputs for Node and .NET - #2590
feat: Add typed structured outputs for Node and .NET#2590SteveSandersonMS wants to merge 4 commits into
Conversation
Generate all language RPC wrappers from the local runtime schema, expose per-run output schemas, and correlate schema-bearing waits using originatingMessageId. Include real-provider recording/replay E2Es through the locally built runtime for raw schemas, tools, steering, batches, and overlapping typed sends. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Auto-committed by java-codegen-check workflow.
Report pinned-schema drift without automatically rewriting draft Java output. Keep failure visibility, retain auto-regeneration for ready PRs, and restore the locally generated Java API after the initial workflow regenerated it against the old published runtime. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
Regenerate event types for all six SDK languages and document isFinalReply. Add real-provider Node and C# direct-send E2Es that parse the final correlated reply while stop hooks block idle, plus regressions preserving SendAndWait rejection on later errors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
SDK Consistency Review — PR #2590 "Add typed structured outputs for Node and .NET"Scope of this PRThis PR adds a high-level typed structured-output API to two SDKs only:
Both additions include new unit tests and real-provider-recorded E2E tests ( Cross-SDK checkAll languages' generated RPC/event wrappers were regenerated from the updated runtime schema in this PR, so the low-level
AssessmentThis looks like the "Bad: Inconsistent feature" pattern described in the review guidelines: a new, clearly generally-useful capability (typed/structured JSON output for This may well be intentional — the PR title explicitly scopes itself to "Node and .NET" and
No inline code issues were found in the Node/.NET implementation itself — this comment is purely about cross-SDK feature-parity tracking.
|
Summary
Prototype SDK APIs for provider-native structured output, paired with
https://github.com/github/copilot-agent-runtime/pull/19652 and related to #1185.
Draft: requires the unreleased runtime at
2c7080a298. The SDK's CLI pin isintentionally unchanged. Do not merge this before the runtime ships and the pin
can be updated.
APIs
Node accepts an explicit JSON Schema or Zod schema on
MessageOptions.responseSchema.That options-based API continues returning an assistant event. A second-argument
Zod schema opts into inferred, parsed, validated results:
C# accepts an explicit
JsonElementonMessageOptions.ResponseSchema, and addsgeneric overloads using the same
Microsoft.Extensions.AI.AIJsonUtilitiesschemainference as custom tools:
Optional
JsonSerializerOptionsgovern both schema inference and deserialization.Source-generated resolver options support hosts with reflection serialization
disabled. Inferred C# schemas request strict output with all properties required
and additional properties disallowed. Deserialization is not full JSON Schema
validation. Both typed helpers throw for missing or unparseable results.
The high-level schema option uses the name
responseandstrict: true. Generatedsession.rpc/session.RpcAPIs expose the full response-format object foradvanced metadata, strictness, and batch sends. Schemas remain opaque at runtime.
Result attribution and completion
turnIdidentifies a model/tool iteration, not the whole queued run. Telemetryinteraction IDs can be reused. The companion runtime change instead adds
assistant.message.data.originatingMessageId, matching the existing ID returnedby
session.send, stable through tools and steering.Schema-bearing waits subscribe before sending, buffer pre-acknowledgement events,
and select the last matching root assistant message without tool requests. Other
queued runs and subagents cannot replace that result. Intermediate messages need
not themselves be parseable JSON.
Direct
sendcallers can now consume a correlated assistant message withisFinalReply: truewithout waiting for idle (IsFinalReplyin C#). The optionalflag marks only the last terminal reply, not intermediate/tool-call messages.
It is a content-selection signal, not a guarantee that later hooks or cleanup
succeed. Subsequent errors still use normal session events.
sendAndWaitkeepsits existing completion behavior, including rejection on a later error.
Completion still uses non-autopilot session idle: later queued work can delay
the result. There is no new per-send completion or error event. Session errors
and aborted idle events after the requested run starts conservatively fail the
wait, even if later queued work caused them. Existing unformatted waits retain
their previous behavior.
Default enqueue delivery owns the schema for the run. Ordinary immediate
steering inherits it; an explicit schema on immediate delivery is rejected.
Generated APIs
Regenerated Node, C#, Python, Go, Rust, and Java wrappers from the local runtime's
emitted RPC and session-event schemas, not a published release. Added a small C#
generator fix and two regressions for singleton
anyOf/oneOfdefinitions.The generated diff also includes genuine contract drift since the SDK's pinned
runtime (factory, model, sandbox, and event additions). It has not been
hand-trimmed or presented as feature-only. Handwritten convenience APIs and E2Es
are limited to Node and C#.
CONTRIBUTING.mddocuments local schema generation andCOPILOT_CLI_PATHwiring.Java's codegen workflow initially auto-committed the old pinned schema over this
draft, removing the new fields. Regenerated those files from the local runtime
again and changed that workflow to report drift rather than auto-rewrite draft
PRs. It still fails for stale output; ready-for-review PRs retain automatic
regeneration. Pinned-schema and packaged-runtime CI are not expected to be green
until the runtime is released and this draft updates its CLI pin.
Validation
gpt-4.1responses through the shared replayproxy and locally built runtime: five Node and four C#. These cover raw
schemas, typed results, generated batch RPCs, tool continuation, schema
clearing, Node terminal tools and steering, and overlapping typed sends in both
languages. Concurrent tests hold a tool call until the second send is visibly
queued; they do not assume RPC admission order.
after a tool call while an agent-stop hook is deliberately held open. Both
prove consumption before idle, then release the hook and observe idle. These
two new captures were recorded from real providers against the rebuilt runtime.
captures passed three consecutive strict replay runs with unchanged hashes.
No model responses or capture YAML were hand-authored.
reflection-enabled host; the resulting capture replays with source-generated
serialization. Source-generated metadata is covered with reflection disabled.
Node build, typecheck, and scoped lint.
languages have a regression proving a final-reply flag does not hide a later
session error from
sendAndWait. Earlier validation also covered 150 C#session-lifetime cases and default inference with reflection enabled.
generator/fetcher cases, 14 Python generated cases, Go generated-package tests,
and Rust library compilation.
Live tests use an explicit supported OpenAI Chat route through the Copilot API,
not independently authenticated provider accounts. The Claude Chat-completions
compatibility route can ignore native format fields; it is not equivalent to the
runtime's Anthropic Messages adapter.
Full SDK suites and other-language E2Es were not run. The broad Node client suite
was not completed; its packaged-runtime prerequisite is separate from this
local-runtime prototype.
C# validation surfaced an existing
Microsoft.Build.Tasks.Gitadvisory(
NU1902); build commands used-p:WarningsNotAsErrors=NU1902to retain the warningwithout blocking this feature's coverage. No dependency versions were changed.