Skip to content

Commit a15fab4

Browse files
Align structured output with released runtime and provider matrix
Use main's polymorphic C# response format, preserve rebased lifecycle coverage, remove the unreleased codegen workaround, and honor the E2E provider matrix. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 8cb2b6d commit a15fab4

9 files changed

Lines changed: 40 additions & 43 deletions

File tree

‎.github/workflows/java-codegen-check.yml‎

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ on:
1010
- 'java/sdk/src/generated/**'
1111
- '.github/workflows/java-codegen-check.yml'
1212
pull_request:
13-
types: [opened, synchronize, reopened, ready_for_review]
1413
paths:
1514
- 'nodejs/package.json'
1615
- 'java/scripts/codegen/**'
@@ -71,18 +70,18 @@ jobs:
7170
echo "✅ Generated files are up-to-date"
7271
fi
7372
74-
# Drafts may intentionally target an unreleased schema; report drift without rewriting them.
75-
- name: Fail on stale generated files without automatic updates
76-
if: steps.check-changes.outputs.changed == 'true' && (github.event_name != 'pull_request' || github.event.pull_request.draft == true)
73+
# --- On push to main: fail if generated files are stale (existing behavior) ---
74+
- name: Fail on stale generated files (push to main)
75+
if: steps.check-changes.outputs.changed == 'true' && github.event_name != 'pull_request'
7776
run: |
7877
echo "::error::Generated files are out of date. Run 'cd java/scripts/codegen && npm run generate' and commit the changes."
7978
git diff
8079
exit 1
8180
82-
# --- On ready PRs: commit regenerated files back and verify build ---
81+
# --- On PR: commit regenerated files back and verify build ---
8382
- name: Commit and push regenerated files to PR branch
8483
id: push-regen
85-
if: steps.check-changes.outputs.changed == 'true' && github.event_name == 'pull_request' && github.event.pull_request.draft == false
84+
if: steps.check-changes.outputs.changed == 'true' && github.event_name == 'pull_request'
8685
continue-on-error: true
8786
env:
8887
GH_TOKEN: ${{ github.token }}

‎CONTRIBUTING.md‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,8 @@ never author model responses by hand. Rerun with `GITHUB_ACTIONS=true` and real
112112
provider credentials removed to require replay instead of forwarding cache
113113
misses upstream. A draft targeting an unreleased runtime should document the
114114
required runtime revision; update the pinned release only after it ships.
115-
Pinned-schema CI can report drift in such a draft. Java codegen reports this
116-
without automatically rewriting draft branches; automatic updates resume once
117-
the pull request is ready for review.
115+
Pinned-schema CI can report drift in such a draft, and Java codegen may
116+
automatically update generated files to match the pinned release.
118117

119118
For recording behind `HTTPS_PROXY`, Node versions that support environment
120119
proxies (including Node 24.20) need `NODE_USE_ENV_PROXY=1` in the test runner's

‎dotnet/README.md‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -362,9 +362,8 @@ using System.Text.Json;
362362
using var schema = JsonDocument.Parse("""
363363
{"type":"object","properties":{"count":{"type":"integer"}},"required":["count"],"additionalProperties":false}
364364
""");
365-
var format = new ResponseFormat
365+
var format = new ResponseFormatJsonSchema
366366
{
367-
Type = "json_schema",
368367
JsonSchema = new JsonSchemaResponseFormat
369368
{
370369
Name = "inventory",
@@ -402,8 +401,8 @@ admission, using the runtime's existing request-size ceiling. This does not
402401
guarantee the schema plus conversation and tools fits the provider's budget.
403402
Use a provider route that enforces JSON Schema: an API-compatible gateway can
404403
ignore unsupported format fields, and the Claude Chat-completions compatibility
405-
route is not equivalent to Anthropic's native Messages endpoint. This preview
406-
requires the unreleased runtime changes; see [local-runtime development](../CONTRIBUTING.md#testing-an-unreleased-runtime-api).
404+
route is not equivalent to Anthropic's native Messages endpoint. The SDK's
405+
pinned CLI release includes the required runtime support.
407406

408407
##### `On(Action<SessionEvent> handler): IDisposable`
409408

‎dotnet/src/Session.cs‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -332,9 +332,8 @@ public async Task<string> SendAsync(MessageOptions options, CancellationToken ca
332332
Traceparent = traceparent,
333333
Tracestate = tracestate,
334334
RequestHeaders = options.RequestHeaders,
335-
ResponseFormat = options.ResponseSchema is { } schema ? new ResponseFormat
335+
ResponseFormat = options.ResponseSchema is { } schema ? new ResponseFormatJsonSchema
336336
{
337-
Type = "json_schema",
338337
JsonSchema = new JsonSchemaResponseFormat
339338
{
340339
Name = "response",

‎dotnet/test/E2E/StructuredOutputE2ETests.cs‎

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,28 +14,30 @@ namespace GitHub.Copilot.Test.E2E;
1414
public partial class StructuredOutputE2ETests(E2ETestFixture fixture, ITestOutputHelper output)
1515
: E2ETestBase(fixture, "structured_output", output)
1616
{
17-
private SessionConfig StructuredSessionConfig() => new()
18-
{
19-
Model = "gpt-4.1",
20-
AvailableTools = [],
21-
Provider = new ProviderConfig
17+
private SessionConfig StructuredSessionConfig() => E2ETestBackendConfiguration.Current != E2ETestBackend.Capi
18+
? new() { AvailableTools = [] }
19+
: new()
2220
{
23-
Type = "openai",
24-
WireApi = "completions",
25-
BaseUrl = Ctx.ProxyUrl,
26-
ModelId = "gpt-4.1",
27-
WireModel = "gpt-4.1",
28-
ApiKey = Environment.GetEnvironmentVariable("GITHUB_ACTIONS") == "true"
21+
Model = "gpt-4.1",
22+
AvailableTools = [],
23+
Provider = new ProviderConfig
24+
{
25+
Type = "openai",
26+
WireApi = "completions",
27+
BaseUrl = Ctx.ProxyUrl,
28+
ModelId = "gpt-4.1",
29+
WireModel = "gpt-4.1",
30+
ApiKey = Environment.GetEnvironmentVariable("GITHUB_ACTIONS") == "true"
2931
? "fake-token-for-e2e-tests"
3032
: Environment.GetEnvironmentVariable("GITHUB_TOKEN") ?? "fake-token-for-e2e-tests",
31-
Headers = new Dictionary<string, string>
32-
{
33-
["Copilot-Integration-Id"] = "copilot-developer-cli",
34-
["Copilot-Harness-Id"] = "copilot-sdk",
35-
["X-GitHub-Api-Version"] = "2026-08-01",
33+
Headers = new Dictionary<string, string>
34+
{
35+
["Copilot-Integration-Id"] = "copilot-developer-cli",
36+
["Copilot-Harness-Id"] = "copilot-sdk",
37+
["X-GitHub-Api-Version"] = "2026-08-01",
38+
},
3639
},
37-
},
38-
};
40+
};
3941

4042
[Fact]
4143
public async Task Infers_Typed_Result_After_Custom_Tool()
@@ -93,9 +95,8 @@ public async Task Sends_Explicit_Schema_For_Message_And_Batch()
9395
"""{"type":"object","properties":{"count":{"type":"integer"},"color":{"type":"string"}},"required":["count","color"],"additionalProperties":false}""");
9496
var accepted = await session.Rpc.SendMessagesAsync(
9597
[new() { Prompt = "There are 42 red widgets in stock." }, new() { Prompt = "Report the widget count and color." }],
96-
responseFormat: new ResponseFormat
98+
responseFormat: new ResponseFormatJsonSchema
9799
{
98-
Type = "json_schema",
99100
JsonSchema = new JsonSchemaResponseFormat
100101
{
101102
Name = "inventory",
@@ -339,9 +340,8 @@ public async Task Rejects_Unsupported_Or_Oversized_Schemas_Before_Admission()
339340
}));
340341
Assert.Contains(message, error.Message);
341342
error = await Assert.ThrowsAnyAsync<Exception>(() =>
342-
session.Rpc.SendMessagesAsync([], responseFormat: new ResponseFormat
343+
session.Rpc.SendMessagesAsync([], responseFormat: new ResponseFormatJsonSchema
343344
{
344-
Type = "json_schema",
345345
JsonSchema = new() { Name = "response", Schema = schema.RootElement },
346346
}));
347347
Assert.Contains(message, error.Message);

‎dotnet/test/Unit/ClientSessionLifetimeTests.cs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2489,6 +2489,7 @@ public Task SendSessionEventAsync(string sessionId, string type, Dictionary<stri
24892489
["id"] = Guid.NewGuid().ToString(),
24902490
["timestamp"] = DateTimeOffset.UtcNow.ToString("O"),
24912491
["parentId"] = null,
2492+
["agentId"] = agentId,
24922493
["type"] = type,
24932494
["data"] = data
24942495
};

‎dotnet/test/Unit/StructuredOutputTests.cs‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,8 @@ public async Task StructuredOutput_Raw_Format_Is_Forwarded_Without_Rewriting(str
2222
await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) });
2323
await using var session = await client.CreateSessionAsync(new SessionConfig());
2424
using var document = JsonDocument.Parse("""{"type":"object","properties":{"value":{"type":"integer"}},"x-provider":{"anything":[true,42,null]}}""");
25-
var format = new ResponseFormat
25+
var format = new ResponseFormatJsonSchema
2626
{
27-
Type = "json_schema",
2827
JsonSchema = new JsonSchemaResponseFormat
2928
{
3029
Name = "answer",

‎nodejs/src/types.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@ export type { SessionFsSqliteStatement } from "./sessionFsProvider.js";
8989
export type { SessionFsSqliteTransactionErrorClass } from "./sessionFsProvider.js";
9090
export { SessionFsSqliteTransactionFailure } from "./sessionFsProvider.js";
9191
export type { LlmInferenceHeaders } from "./generated/rpc.js";
92-
export type { PermissionDecisionSource } from "./generated/session-events.js";
9392
export type {
9493
PermissionDecisionContext,
9594
PermissionDecisionOutcome,

‎scripts/codegen/csharp.test.ts‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,15 @@ for (const keyword of ["anyOf", "oneOf"] as const) {
5050
},
5151
});
5252

53-
assert.match(code, /public sealed class ResponseFormat\b/);
53+
assert.match(code, /public partial class ResponseFormat\b/);
54+
assert.match(code, /public partial class ResponseFormatJsonSchema : ResponseFormat/);
55+
assert.match(code, /\[JsonDerivedType\(typeof\(ResponseFormatJsonSchema\), "json_schema"\)\]/);
5456
assert.match(code, /A provider-native output format\./);
5557
assert.match(code, /public ResponseFormat\? ResponseFormat/);
5658
assert.match(code, /public ResponseFormat RequiredFormat/);
57-
assert.match(code, /public JsonSchemaResponseFormat JsonSchema/);
59+
assert.match(code, /public required JsonSchemaResponseFormat JsonSchema/);
5860
assert.match(code, /public JsonElement Schema/);
5961
assert.match(code, /public bool\? Strict/);
60-
assert.equal(code.match(/public sealed class ResponseFormat\b/g)?.length, 1);
62+
assert.equal(code.match(/public partial class ResponseFormat\b/g)?.length, 1);
6163
});
6264
}

0 commit comments

Comments
 (0)