Skip to content

Commit 6e209be

Browse files
patnikoCopilot
authored andcommitted
Add reasoningEffort to setModel/session.model.switchTo across all SDKs
All four SDKs now support passing reasoningEffort when switching models mid-session via setModel(). The parameter is optional and backward-compatible. - Node.js: setModel(model, { reasoningEffort? }) - Python: set_model(model, *, reasoning_effort=None) - Go: SetModel(ctx, model, opts ...*SetModelOptions) - .NET: SetModelAsync(model, reasoningEffort?, cancellationToken?) Fixes #687 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent a0bbde9 commit 6e209be

7 files changed

Lines changed: 74 additions & 11 deletions

File tree

dotnet/src/Session.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -720,15 +720,25 @@ await InvokeRpcAsync<object>(
720720
/// The new model takes effect for the next message. Conversation history is preserved.
721721
/// </summary>
722722
/// <param name="model">Model ID to switch to (e.g., "gpt-4.1").</param>
723+
/// <param name="reasoningEffort">Optional reasoning effort level (e.g., "low", "medium", "high", "xhigh").</param>
723724
/// <param name="cancellationToken">Optional cancellation token.</param>
724725
/// <example>
725726
/// <code>
726727
/// await session.SetModelAsync("gpt-4.1");
728+
/// await session.SetModelAsync("claude-sonnet-4.6", SessionModelSwitchToRequestReasoningEffort.High);
727729
/// </code>
728730
/// </example>
729-
public async Task SetModelAsync(string model, CancellationToken cancellationToken = default)
731+
public async Task SetModelAsync(string model, SessionModelSwitchToRequestReasoningEffort? reasoningEffort = null, CancellationToken cancellationToken = default)
730732
{
731-
await Rpc.Model.SwitchToAsync(model, cancellationToken: cancellationToken);
733+
await Rpc.Model.SwitchToAsync(model, reasoningEffort, cancellationToken);
734+
}
735+
736+
/// <summary>
737+
/// Changes the model for this session (backward-compatible overload).
738+
/// </summary>
739+
public Task SetModelAsync(string model, CancellationToken cancellationToken)
740+
{
741+
return SetModelAsync(model, reasoningEffort: null, cancellationToken);
732742
}
733743

734744
/// <summary>

dotnet/test/RpcTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public async Task Should_Call_Session_Rpc_Model_SwitchTo()
7373
Assert.NotNull(before.ModelId);
7474

7575
// Switch to a different model
76-
var result = await session.Rpc.Model.SwitchToAsync(modelId: "gpt-4.1");
76+
var result = await session.Rpc.Model.SwitchToAsync(modelId: "gpt-4.1", reasoningEffort: null);
7777
Assert.Equal("gpt-4.1", result.ModelId);
7878

7979
// Verify the switch persisted

go/internal/e2e/rpc_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ func TestSessionRpc(t *testing.T) {
201201
t.Fatalf("Failed to create session: %v", err)
202202
}
203203

204-
if err := session.SetModel(t.Context(), "gpt-4.1"); err != nil {
204+
if err := session.SetModel(t.Context(), "gpt-4.1", nil); err != nil {
205205
t.Fatalf("SetModel returned error: %v", err)
206206
}
207207
})

go/session.go

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -737,16 +737,31 @@ func (s *Session) Abort(ctx context.Context) error {
737737
return nil
738738
}
739739

740+
// SetModelOptions configures optional parameters for SetModel.
741+
type SetModelOptions struct {
742+
// ReasoningEffort sets the reasoning effort level for the new model (e.g., "low", "medium", "high", "xhigh").
743+
ReasoningEffort rpc.ReasoningEffort
744+
}
745+
740746
// SetModel changes the model for this session.
741747
// The new model takes effect for the next message. Conversation history is preserved.
748+
// Pass nil for opts if no additional options are needed.
742749
//
743750
// Example:
744751
//
745-
// if err := session.SetModel(context.Background(), "gpt-4.1"); err != nil {
752+
// if err := session.SetModel(context.Background(), "gpt-4.1", nil); err != nil {
746753
// log.Printf("Failed to set model: %v", err)
747754
// }
748-
func (s *Session) SetModel(ctx context.Context, model string) error {
749-
_, err := s.RPC.Model.SwitchTo(ctx, &rpc.SessionModelSwitchToParams{ModelID: model})
755+
// if err := session.SetModel(context.Background(), "claude-sonnet-4.6", &SetModelOptions{ReasoningEffort: "high"}); err != nil {
756+
// log.Printf("Failed to set model: %v", err)
757+
// }
758+
func (s *Session) SetModel(ctx context.Context, model string, opts *SetModelOptions) error {
759+
params := &rpc.SessionModelSwitchToParams{ModelID: model}
760+
if opts != nil && opts.ReasoningEffort != "" {
761+
re := opts.ReasoningEffort
762+
params.ReasoningEffort = &re
763+
}
764+
_, err := s.RPC.Model.SwitchTo(ctx, params)
750765
if err != nil {
751766
return fmt.Errorf("failed to set model: %w", err)
752767
}

nodejs/src/session.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -718,14 +718,19 @@ export class CopilotSession {
718718
* The new model takes effect for the next message. Conversation history is preserved.
719719
*
720720
* @param model - Model ID to switch to
721+
* @param options - Optional settings for the new model
721722
*
722723
* @example
723724
* ```typescript
724725
* await session.setModel("gpt-4.1");
726+
* await session.setModel("claude-sonnet-4.6", { reasoningEffort: "high" });
725727
* ```
726728
*/
727-
async setModel(model: string): Promise<void> {
728-
await this.rpc.model.switchTo({ modelId: model });
729+
async setModel(
730+
model: string,
731+
options?: { reasoningEffort?: "low" | "medium" | "high" | "xhigh" }
732+
): Promise<void> {
733+
await this.rpc.model.switchTo({ modelId: model, ...options });
729734
}
730735

731736
/**

nodejs/test/client.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,31 @@ describe("CopilotClient", () => {
123123
spy.mockRestore();
124124
});
125125

126+
it("sends reasoningEffort with session.model.switchTo when provided", async () => {
127+
const client = new CopilotClient();
128+
await client.start();
129+
onTestFinished(() => client.forceStop());
130+
131+
const session = await client.createSession({ onPermissionRequest: approveAll });
132+
133+
const spy = vi
134+
.spyOn((client as any).connection!, "sendRequest")
135+
.mockImplementation(async (method: string, _params: any) => {
136+
if (method === "session.model.switchTo") return {};
137+
throw new Error(`Unexpected method: ${method}`);
138+
});
139+
140+
await session.setModel("claude-sonnet-4.6", { reasoningEffort: "high" });
141+
142+
expect(spy).toHaveBeenCalledWith("session.model.switchTo", {
143+
sessionId: session.sessionId,
144+
modelId: "claude-sonnet-4.6",
145+
reasoningEffort: "high",
146+
});
147+
148+
spy.mockRestore();
149+
});
150+
126151
describe("URL parsing", () => {
127152
it("should parse port-only URL format", () => {
128153
const client = new CopilotClient({

python/copilot/session.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -728,7 +728,7 @@ async def abort(self) -> None:
728728
"""
729729
await self._client.request("session.abort", {"sessionId": self.session_id})
730730

731-
async def set_model(self, model: str) -> None:
731+
async def set_model(self, model: str, *, reasoning_effort: str | None = None) -> None:
732732
"""
733733
Change the model for this session.
734734
@@ -737,14 +737,22 @@ async def set_model(self, model: str) -> None:
737737
738738
Args:
739739
model: Model ID to switch to (e.g., "gpt-4.1", "claude-sonnet-4").
740+
reasoning_effort: Optional reasoning effort level for the new model
741+
(e.g., "low", "medium", "high", "xhigh").
740742
741743
Raises:
742744
Exception: If the session has been destroyed or the connection fails.
743745
744746
Example:
745747
>>> await session.set_model("gpt-4.1")
748+
>>> await session.set_model("claude-sonnet-4.6", reasoning_effort="high")
746749
"""
747-
await self.rpc.model.switch_to(SessionModelSwitchToParams(model_id=model))
750+
await self.rpc.model.switch_to(
751+
SessionModelSwitchToParams(
752+
model_id=model,
753+
reasoning_effort=reasoning_effort,
754+
)
755+
)
748756

749757
async def log(
750758
self,

0 commit comments

Comments
 (0)