Fix CAPI reasoning E2E fixtures - #2181
Conversation
Use gpt-5.4 for tests that explicitly configure high reasoning effort, and advertise that model in the streaming replay fixture. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9b80f6cb-b851-4598-8294-f4431c6f3143
There was a problem hiding this comment.
Pull request overview
Updates .NET E2E tests to use a reasoning-capable model.
Changes:
- Switches reasoning-effort tests to
gpt-5.4. - Aligns the streaming replay fixture’s model catalog.
Show a summary per file
| File | Description |
|---|---|
test/snapshots/streaming_fidelity/should_emit_streaming_deltas_with_reasoning_effort_configured.yaml |
Advertises gpt-5.4. |
dotnet/test/E2E/StreamingFidelityE2ETests.cs |
Configures the streaming test with gpt-5.4. |
dotnet/test/E2E/SessionE2ETests.cs |
Uses and asserts gpt-5.4 during model switching. |
Review details
- Files reviewed: 3/3 changed files
- Comments generated: 0
- Review effort level: Balanced
This comment has been minimized.
This comment has been minimized.
|
@copilot does the same issue exist in any of the other 5 language test suites? |
… Java test suites The same issue from .NET (using models that don't support configurable reasoning with explicit reasoning_effort) existed in the other five SDKs: - setModel tests used gpt-4.1 (non-reasoning) with reasoning_effort=high in Node.js, Python, Go, and Rust — switched to gpt-5.4 in all four - Streaming fidelity tests created sessions without an explicit model; since the shared snapshot was already updated to gpt-5.4 these would have worked, but explicitly specifying model=gpt-5.4 matches the .NET pattern and makes the reasoning-capability requirement self-documenting (Node.js, Python, Go, Rust, Java) - Java has no setModel-with-reasoning test but the streaming fidelity fix still applies Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
Yes — the same issue existed in all five other SDKs. Fixed in commit
Streaming fidelity tests (no explicit model → implicit |
This comment has been minimized.
This comment has been minimized.
Use a fresh client for the streaming reasoning test so its gpt-5.4 model catalog is not shadowed by the shared client's earlier cached fixture. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9b80f6cb-b851-4598-8294-f4431c6f3143
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Review details
Suppressed comments (9)
dotnet/test/E2E/StreamingFidelityE2ETests.cs:158
- This still uses the class-shared runtime, whose 30-minute
/modelscache is normally primed by an earlier streaming fixture that advertises onlyclaude-sonnet-4.5. Reconfiguring the proxy to this snapshot does not refresh that cache, sogpt-5.4may be rejected or reverted and this E2E remains order-dependent. Use an isolatedE2ETestContexthere, asRpcSessionStateE2ETests.Should_Call_Session_Rpc_Model_SwitchTodoes.
Model = "gpt-5.4",
python/e2e/test_streaming_fidelity_e2e.py:166
- The module-scoped
ctx.clientretains the/modelsresult from whichever earlier test starts it, and the earlier streaming snapshots advertise onlyclaude-sonnet-4.5. Updating this test's proxy snapshot therefore does not makegpt-5.4visible to that runtime, leaving this test order-dependent. Run this case in a freshE2ETestContext, following the isolated model-switch test intest_rpc_session_state_e2e.py.
session = await ctx.client.create_session(
on_permission_request=PermissionHandler.approve_all,
model="gpt-5.4",
rust/tests/e2e/session.rs:979
- The shared replay fixture
test/snapshots/session/should_set_model_with_reasoningeffort.yamlstill advertises onlyclaude-sonnet-4.5. Model switching revalidates against that catalog and can emit this change event before reverting an unavailable target, so these assertions can pass withoutgpt-5.4ever becoming active. Addgpt-5.4to the fixture'smodelslist so this actually exercises a reasoning-capable model.
"gpt-5.4",
Some(SetModelOptions::default().with_reasoning_effort("high")),
)
.await
.expect("set model");
let event = model_changed.await.expect("model change task");
let data = event
.typed_data::<SessionModelChangeData>()
.expect("session.model_change data");
assert_eq!(data.new_model, "gpt-5.4");
go/internal/e2e/session_e2e_test.go:1083
- The shared replay fixture
test/snapshots/session/should_set_model_with_reasoningeffort.yamlstill advertises onlyclaude-sonnet-4.5. Model switching revalidates against that catalog and can emit this change event before reverting an unavailable target, so these assertions can pass withoutgpt-5.4ever becoming active. Addgpt-5.4to the fixture'smodelslist so this actually exercises a reasoning-capable model.
if err := session.SetModel(t.Context(), "gpt-5.4", &copilot.SetModelOptions{ReasoningEffort: copilot.String("high")}); err != nil {
t.Fatalf("SetModel returned error: %v", err)
}
select {
case evt := <-modelChanged:
md, mdOk := evt.Data.(*copilot.SessionModelChangeData)
if !mdOk || md.NewModel != "gpt-5.4" {
t.Errorf("Expected newModel 'gpt-5.4', got %v", evt.Data)
go/internal/e2e/streaming_fidelity_e2e_test.go:294
- This subtest reuses the outer client, whose
/modelscache is already populated by an earlier streaming subtest with onlyclaude-sonnet-4.5. Changing the replay snapshot does not refresh that runtime cache, so selectinggpt-5.4is order-dependent. Give this subtest its own context/client, as the model-switch subtest inrpc_session_state_e2e_test.godoes.
session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{
OnPermissionRequest: copilot.PermissionHandler.ApproveAll,
Model: "gpt-5.4",
nodejs/test/e2e/streaming_fidelity.e2e.test.ts:153
- Creating another client wrapper is insufficient in the in-process E2E matrix: both clients load into the same host process and use the same token/proxy URL, so the runtime's process-wide
/modelscache retains the earlierclaude-sonnet-4.5catalog. This case remains transport-dependent even though its snapshot advertisesgpt-5.4. Isolate it with its owncreateSdkTestContext()(and therefore a unique proxy/cache key), as the RPC model-switch test does.
const reasoningClient = createClient();
onTestFinished(() => reasoningClient.stop());
const session = await reasoningClient.createSession({
onPermissionRequest: approveAll,
model: "gpt-5.4",
nodejs/test/e2e/session.e2e.test.ts:975
- The model-switch fixture still advertises only
claude-sonnet-4.5, and this test also uses the describe-shared runtime whose/modelscache may already come from another snapshot.session.model.switchTocan emit the change event and then revert a target absent from that cached catalog, so these assertions can pass withoutgpt-5.4becoming active. Addgpt-5.4to the shared snapshot and run this case in a dedicatedcreateSdkTestContext(), following the isolated RPC model-switch test.
await session.setModel("gpt-5.4", { reasoningEffort: "high" });
const event = await modelChangePromise;
expect(event.data.newModel).toBe("gpt-5.4");
python/e2e/test_session_e2e.py:701
- The model-switch fixture still advertises only
claude-sonnet-4.5, and the module-scoped client may already have cached another snapshot's catalog. Model switching can emit this event and then revert a target absent from the cached catalog, so the test can pass withoutgpt-5.4becoming active. Addgpt-5.4to the snapshot and use a freshE2ETestContext, following the isolated RPC model-switch test.
await session.set_model("gpt-5.4", reasoning_effort="high")
data = await asyncio.wait_for(model_change_event, timeout=30)
assert data.new_model == "gpt-5.4"
dotnet/test/E2E/SessionE2ETests.cs:621
- The model-switch fixture still advertises only
claude-sonnet-4.5, and the class-shared runtime may already have cached another snapshot's catalog. Model switching can emit this event and then revert a target absent from the cached catalog, so the test can pass withoutgpt-5.4becoming active. Addgpt-5.4to the snapshot and use an isolatedE2ETestContext, followingShould_Call_Session_Rpc_Model_SwitchTo.
await session.SetModelAsync("gpt-5.4", "high");
var modelChanged = await modelChangedTask;
Assert.Equal("gpt-5.4", modelChanged.Data.NewModel);
- Files reviewed: 12/12 changed files
- Comments generated: 0 new
- Review effort level: Balanced
Give the streaming reasoning test a dedicated harness context so both stdio and in-process transports use its gpt-5.4 model catalog. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9b80f6cb-b851-4598-8294-f4431c6f3143
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Review details
Suppressed comments (4)
go/internal/e2e/streaming_fidelity_e2e_test.go:294
- The parent test reuses one client across all subtests, so
/modelshas already been cached from the firstclaude-sonnet-4.5fixture before this snapshot is selected. The cache is not invalidated whenConfigureForTestchanges snapshots, leavinggpt-5.4unavailable. Give this subtest its ownTestContextand client, following the isolated model-switch subtest inrpc_session_state_e2e_test.go.
Model: "gpt-5.4",
dotnet/test/E2E/StreamingFidelityE2ETests.cs:155
E2ETestFixture.Clientis persistent for this class, while the runtime caches/modelsfor 30 minutes. Whichever test initializes the cache first makes either thisgpt-5.4fixture or the other Claude fixtures unavailable, so this class becomes order-dependent. Create an isolatedE2ETestContext/client for this test, asRpcSessionStateE2ETests.Should_Call_Session_Rpc_Model_SwitchTodoes.
Model = "gpt-5.4",
python/e2e/test_streaming_fidelity_e2e.py:166
- This test still uses the module-scoped
ctx/CLI, whose 30-minute/modelscache was populated by an earlier streaming fixture withclaude-sonnet-4.5. Reconfiguring the proxy to this snapshot does not refresh that cache, sogpt-5.4is unavailable and the reasoning test is order-dependent. Run this case in an isolatedE2ETestContext, as the model-switch test does.
model="gpt-5.4",
java/src/test/java/com/github/copilot/StreamingFidelityTest.java:257
- The class shares one proxy URL across tests, but
/modelsis cached for 30 minutes by(auth, base URL). Switching this snapshot to onlygpt-5.4therefore makes the result depend on which test populates the cache first (and is especially problematic for the in-process backend). Use a dedicatedE2ETestContext/proxy for this test rather than the class-levelctx.
.setModel("gpt-5.4").setStreaming(true).setReasoningEffort("high"))
- Files reviewed: 12/12 changed files
- Comments generated: 5
- Review effort level: Balanced
Load the gpt-5.4 catalog in dedicated test contexts so model-switch assertions exercise the selected reasoning-capable model across SDKs. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9b80f6cb-b851-4598-8294-f4431c6f3143
Cross-SDK Consistency Review ✅This PR updates E2E test fixtures symmetrically across all six SDKs:
Both shared YAML snapshots ( No cross-SDK consistency issues found. The changes are well-aligned across all language implementations.
|
There was a problem hiding this comment.
Review details
Suppressed comments (3)
python/e2e/test_streaming_fidelity_e2e.py:166
- This still uses the module-scoped
ctx.client, whose 30-minute/modelscache is warmed by earlier streaming tests from snapshots that advertise onlyclaude-sonnet-4.5. Reconfiguring the same proxy to this GPT-only snapshot does not change that cache key, so creating this session with explicithighreasoning can fail as unsupported in a full module run. Give this test its ownE2ETestContext, as the model-switch test intest_session_e2e.pynow does.
model="gpt-5.4",
go/internal/e2e/streaming_fidelity_e2e_test.go:294
- The enclosing test reuses one client across all subtests, so earlier Claude-only streaming snapshots populate the runtime's
/modelscache for this proxy URL. This GPT-only snapshot cannot refresh that cached catalog, and explicithighreasoning may therefore be rejected. Create a fresh context/client inside this subtest so it has a distinct proxy/cache key.
Model: "gpt-5.4",
dotnet/test/E2E/StreamingFidelityE2ETests.cs:158
CreateSessionAsyncuses this class fixture's persistent client and proxy, whose/modelscache may already have been populated by another streaming test's Claude-only snapshot. Switching the proxy fixture here does not change the cache key, so the full class run can rejecthighforgpt-5.4. Use an isolated context/client for this test, matchingShould_Set_Model_With_ReasoningEffort.
Model = "gpt-5.4",
- Files reviewed: 13/13 changed files
- Comments generated: 0 new
- Review effort level: Balanced
Give the Python, Go, and .NET streaming reasoning tests dedicated proxy contexts so cached model catalogs cannot hide gpt-5.4. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9b80f6cb-b851-4598-8294-f4431c6f3143
Summary
The C# CAPI E2E fixtures configured explicit
highreasoning effort with models that do not support configurable reasoning:StreamingFidelityE2ETests.Should_Emit_Streaming_Deltas_With_Reasoning_Effort_Configuredinherited the fixture default,claude-sonnet-4.5.SessionE2ETests.Should_Set_Model_With_ReasoningEffortswitched togpt-4.1.Both tests now use the reasoning-capable
gpt-5.4model. The streaming replay fixture also advertisesgpt-5.4so the recorded CAPI model catalog matches the test configuration. Assertions that verifyhighreasoning effort propagation remain unchanged.This preserves the intended runtime contract introduced by github/copilot-agent-runtime commit
6c6125edb8/ PR #12016: explicit unsupported CAPI reasoning efforts fail instead of being silently discarded or clamped.Validation
dotnet test dotnet/test/GitHub.Copilot.SDK.Test.csproj --no-restore --filter 'FullyQualifiedName~StreamingFidelityE2ETests.Should_Emit_Streaming_Deltas_With_Reasoning_Effort_Configured|FullyQualifiedName~SessionE2ETests.Should_Set_Model_With_ReasoningEffort'(2 passed)dotnet format dotnet/test/GitHub.Copilot.SDK.Test.csproj --verify-no-changes --no-restore