Skip to content

Commit a11223e

Browse files
authored
Merge pull request #257 from Opencode-DCP/fix/anthropic-injection-reasoning-check
fix: require reasoning block before injecting context for Claude models
2 parents 3926883 + 549aac2 commit a11223e

File tree

3 files changed

+51
-3
lines changed

3 files changed

+51
-3
lines changed

lib/messages/inject.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import {
77
extractParameterKey,
88
buildToolIdList,
99
createSyntheticAssistantMessageWithToolPart,
10+
isIgnoredUserMessage,
11+
hasReasoningInCurrentAssistantTurn,
1012
} from "./utils"
1113
import { getFilePathFromParameters, isProtectedFilePath } from "../protected-file-patterns"
1214
import { getLastUserMessage } from "../shared-utils"
@@ -142,11 +144,22 @@ export const insertPruneToolContext = (
142144
const modelID = userInfo.model.modelID
143145
const isGitHubCopilot =
144146
providerID === "github-copilot" || providerID === "github-copilot-enterprise"
147+
148+
// TODO: This can probably be improved further to only trigger for the appropriate thinking settings
149+
// This setting is also potentially only necessary for claude subscription, API seems to not need this
150+
// validation. See more here: https://platform.claude.com/docs/en/build-with-claude/extended-thinking
145151
const isAnthropic = modelID.includes("claude")
146152

147-
if (isGitHubCopilot || isAnthropic) {
153+
if (isGitHubCopilot) {
148154
const lastMessage = messages[messages.length - 1]
149-
if (lastMessage?.info?.role === "user") {
155+
if (lastMessage?.info?.role === "user" && !isIgnoredUserMessage(lastMessage)) {
156+
return
157+
}
158+
}
159+
160+
// Anthropic extended thinking models require a thinking block at the start of its turn
161+
if (isAnthropic) {
162+
if (!hasReasoningInCurrentAssistantTurn(messages)) {
150163
return
151164
}
152165
}

lib/messages/utils.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,3 +193,37 @@ export function buildToolIdList(
193193
}
194194
return toolIds
195195
}
196+
197+
export const isIgnoredUserMessage = (message: WithParts): boolean => {
198+
if (!message.parts || message.parts.length === 0) {
199+
return true
200+
}
201+
202+
for (const part of message.parts) {
203+
if (!(part as any).ignored) {
204+
return false
205+
}
206+
}
207+
208+
return true
209+
}
210+
211+
export const hasReasoningInCurrentAssistantTurn = (messages: WithParts[]): boolean => {
212+
for (let i = messages.length - 1; i >= 0; i--) {
213+
const message = messages[i]
214+
if (message.info?.role === "user") {
215+
if (isIgnoredUserMessage(message)) {
216+
continue
217+
}
218+
return false
219+
}
220+
if (message.info?.role === "assistant" && message.parts) {
221+
for (const part of message.parts) {
222+
if (part.type === "reasoning") {
223+
return true
224+
}
225+
}
226+
}
227+
}
228+
return false
229+
}

lib/shared-utils.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { SessionState, WithParts } from "./state"
2+
import { isIgnoredUserMessage } from "./messages/utils"
23

34
export const isMessageCompacted = (state: SessionState, msg: WithParts): boolean => {
45
return msg.info.time.created < state.lastCompaction
@@ -7,7 +8,7 @@ export const isMessageCompacted = (state: SessionState, msg: WithParts): boolean
78
export const getLastUserMessage = (messages: WithParts[]): WithParts | null => {
89
for (let i = messages.length - 1; i >= 0; i--) {
910
const msg = messages[i]
10-
if (msg.info.role === "user") {
11+
if (msg.info.role === "user" && !isIgnoredUserMessage(msg)) {
1112
return msg
1213
}
1314
}

0 commit comments

Comments
 (0)