Skip to content

Commit 5954625

Browse files
committed
fix(core): preserve max reasoning effort in edit requests
1 parent 3ed02da commit 5954625

3 files changed

Lines changed: 53 additions & 4 deletions

File tree

‎packages/core/src/tests/openai-thinking.test.ts‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { test } from "node:test";
22
import assert from "node:assert/strict";
3+
import OpenAI from "openai";
34
import { buildThinkingRequestOptions } from "../common/openai-thinking";
45

56
test("buildThinkingRequestOptions explicitly disables thinking", () => {
@@ -41,3 +42,36 @@ test("buildThinkingRequestOptions accepts low reasoning effort", () => {
4142
reasoning_effort: "low",
4243
});
4344
});
45+
46+
test("OpenAI SDK sends thinking options at the top level", async () => {
47+
let body: Record<string, unknown> = {};
48+
const client = new OpenAI({
49+
apiKey: "test-key",
50+
maxRetries: 0,
51+
fetch: async (_url, init) => {
52+
body = JSON.parse(String(init?.body));
53+
return new Response('{"choices":[]}', { headers: { "content-type": "application/json" } });
54+
},
55+
});
56+
57+
for (const [enabled, effort] of [
58+
[true, "low"],
59+
[true, "high"],
60+
[true, "max"],
61+
[true, undefined],
62+
[false, "max"],
63+
] as const) {
64+
await client.chat.completions.create({
65+
model: "deepseek-flash",
66+
messages: [{ role: "user", content: "hello" }],
67+
...buildThinkingRequestOptions(enabled, undefined, effort),
68+
} as OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming);
69+
70+
assert.deepEqual(body, {
71+
model: "deepseek-flash",
72+
messages: [{ role: "user", content: "hello" }],
73+
thinking: { type: enabled ? "enabled" : "disabled" },
74+
...(enabled ? { reasoning_effort: effort ?? "max" } : {}),
75+
});
76+
}
77+
});

‎packages/core/src/tests/tool-handlers.test.ts‎

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,7 @@ test("Edit appends an LLM diagnosis when old_string is not found", async () => {
631631
assert.ok(snippet);
632632

633633
let llmCalls = 0;
634+
let llmRequest: Record<string, unknown> | undefined;
634635
let prompt = "";
635636
const editResult = await handleEditTool(
636637
{
@@ -645,6 +646,7 @@ test("Edit appends an LLM diagnosis when old_string is not found", async () => {
645646
completions: {
646647
create: async (request: { messages?: Array<{ content?: string }> }) => {
647648
llmCalls += 1;
649+
llmRequest = request;
648650
prompt = String(request.messages?.[1]?.content ?? "");
649651
return {
650652
choices: [
@@ -661,13 +663,17 @@ test("Edit appends an LLM diagnosis when old_string is not found", async () => {
661663
},
662664
} as any,
663665
model: "test-model",
664-
thinkingEnabled: false,
666+
thinkingEnabled: true,
667+
reasoningEffort: "max",
665668
}),
666669
})
667670
);
668671

669672
assert.equal(editResult.ok, false);
670673
assert.equal(llmCalls, 1);
674+
assert.deepEqual(llmRequest?.thinking, { type: "enabled" });
675+
assert.equal(llmRequest?.reasoning_effort, "max");
676+
assert.equal("extra_body" in llmRequest!, false);
671677
assert.equal(
672678
editResult.error,
673679
"old_string not found in file. The requested function name is computeTotal, but the snippet contains computeSubtotal."
@@ -972,6 +978,7 @@ test("Edit uses LLM correction when straight and curly quotation marks differ",
972978
const sessionId = "quotation-mark-correction";
973979
const snippet = await readSnippet(filePath, sessionId, workspace);
974980
let llmCalls = 0;
981+
let llmRequest: Record<string, unknown> | undefined;
975982
const editResult = await handleEditTool(
976983
{
977984
snippet_id: snippet.id,
@@ -989,6 +996,7 @@ test("Edit uses LLM correction when straight and curly quotation marks differ",
989996
completions: {
990997
create: async (request: { messages?: Array<{ content?: string }> }) => {
991998
llmCalls += 1;
999+
llmRequest = request;
9921000
assert.match(String(request.messages?.[0]?.content ?? ""), /the only problem is quotation mark/);
9931001
return {
9941002
choices: [
@@ -1013,13 +1021,17 @@ test("Edit uses LLM correction when straight and curly quotation marks differ",
10131021
},
10141022
} as any,
10151023
model: "test-model",
1016-
thinkingEnabled: false,
1024+
thinkingEnabled: true,
1025+
reasoningEffort: "max",
10171026
}),
10181027
})
10191028
);
10201029

10211030
assert.equal(editResult.ok, true);
10221031
assert.equal(llmCalls, 1);
1032+
assert.deepEqual(llmRequest?.thinking, { type: "enabled" });
1033+
assert.equal(llmRequest?.reasoning_effort, "max");
1034+
assert.equal("extra_body" in llmRequest!, false);
10231035
assert.equal(editResult.metadata?.matched_via, "llm_escape_correction");
10241036
assert.equal(
10251037
fs.readFileSync(filePath, "utf8"),

‎packages/core/src/tools/edit-handler.ts‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as fs from "fs";
22
import { z } from "zod";
3+
import type { ChatCompletionCreateParamsNonStreaming } from "openai/resources/chat/completions";
34
import { buildThinkingRequestOptions } from "../common/openai-thinking";
45
import type { ToolExecutionContext, ToolExecutionResult } from "./executor";
56
import {
@@ -662,6 +663,7 @@ async function inferOldStringNotFoundReasonWithLLM(
662663

663664
try {
664665
context.signal?.throwIfAborted();
666+
// DeepSeek supports "max", which is absent from the OpenAI SDK types.
665667
const response = await client.chat.completions.create(
666668
{
667669
model,
@@ -692,7 +694,7 @@ async function inferOldStringNotFoundReasonWithLLM(
692694
},
693695
],
694696
...buildThinkingRequestOptions(thinkingEnabled, baseURL, reasoningEffort),
695-
},
697+
} as ChatCompletionCreateParamsNonStreaming,
696698
{ signal: context.signal }
697699
);
698700
context.signal?.throwIfAborted();
@@ -748,6 +750,7 @@ async function correctEscapedStringsWithLLM(
748750
try {
749751
const problemDescription = describeCorrectionProblems(oldString, matchedText);
750752
context.signal?.throwIfAborted();
753+
// DeepSeek supports "max", which is absent from the OpenAI SDK types.
751754
const response = await client.chat.completions.create(
752755
{
753756
model,
@@ -777,7 +780,7 @@ async function correctEscapedStringsWithLLM(
777780
},
778781
],
779782
...buildThinkingRequestOptions(thinkingEnabled, baseURL, reasoningEffort),
780-
},
783+
} as ChatCompletionCreateParamsNonStreaming,
781784
{ signal: context.signal }
782785
);
783786
context.signal?.throwIfAborted();

0 commit comments

Comments
 (0)