Skip to content

Commit 3a1bfd6

Browse files
committed
feat: use synthetic assistant messages for non-DeepSeek/Kimi injection
1 parent 5166df3 commit 3a1bfd6

File tree

2 files changed

+71
-4
lines changed

2 files changed

+71
-4
lines changed

lib/messages/inject.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import {
88
buildToolIdList,
99
createSyntheticTextPart,
1010
createSyntheticToolPart,
11+
createSyntheticAssistantMessage,
1112
isIgnoredUserMessage,
13+
isDeepSeekOrKimi,
1214
} from "./utils"
1315
import { getFilePathFromParameters, isProtectedFilePath } from "../protected-file-patterns"
1416
import { getLastUserMessage, isMessageCompacted } from "../shared-utils"
@@ -182,6 +184,7 @@ export const insertPruneToolContext = (
182184
}
183185

184186
const userInfo = lastUserMessage.info as UserMessage
187+
const variant = state.variant ?? userInfo.variant
185188

186189
const lastNonIgnoredMessage = messages.findLast(
187190
(msg) => !(msg.info.role === "user" && isIgnoredUserMessage(msg)),
@@ -199,12 +202,24 @@ export const insertPruneToolContext = (
199202
const textPart = createSyntheticTextPart(lastNonIgnoredMessage, combinedContent)
200203
lastNonIgnoredMessage.parts.push(textPart)
201204
} else {
202-
// Append tool part to existing assistant message. This approach works universally across
203-
// models including DeepSeek and Kimi which don't output reasoning parts following an
205+
// For non-user message case: push a new synthetic assistant message or append tool part
206+
// for DeepSeek/Kimi. DeepSeek and Kimi don't output reasoning parts following an
204207
// assistant injection containing text parts. Tool parts appended to the last assistant
205208
// message are the safest way to inject context without disrupting model behavior.
209+
const providerID = userInfo.model?.providerID || ""
206210
const modelID = userInfo.model?.modelID || ""
207-
const toolPart = createSyntheticToolPart(lastNonIgnoredMessage, combinedContent, modelID)
208-
lastNonIgnoredMessage.parts.push(toolPart)
211+
212+
if (isDeepSeekOrKimi(providerID, modelID)) {
213+
const toolPart = createSyntheticToolPart(
214+
lastNonIgnoredMessage,
215+
combinedContent,
216+
modelID,
217+
)
218+
lastNonIgnoredMessage.parts.push(toolPart)
219+
} else {
220+
messages.push(
221+
createSyntheticAssistantMessage(lastUserMessage, combinedContent, variant),
222+
)
223+
}
209224
}
210225
}

lib/messages/utils.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,17 @@ const isGeminiModel = (modelID: string): boolean => {
1313
return lowerModelID.includes("gemini")
1414
}
1515

16+
export const isDeepSeekOrKimi = (providerID: string, modelID: string): boolean => {
17+
const lowerProviderID = providerID.toLowerCase()
18+
const lowerModelID = modelID.toLowerCase()
19+
return (
20+
lowerProviderID.includes("deepseek") ||
21+
lowerProviderID.includes("kimi") ||
22+
lowerModelID.includes("deepseek") ||
23+
lowerModelID.includes("kimi")
24+
)
25+
}
26+
1627
export const createSyntheticUserMessage = (
1728
baseMessage: WithParts,
1829
content: string,
@@ -45,6 +56,47 @@ export const createSyntheticUserMessage = (
4556
}
4657
}
4758

59+
export const createSyntheticAssistantMessage = (
60+
baseMessage: WithParts,
61+
content: string,
62+
variant?: string,
63+
): WithParts => {
64+
const userInfo = baseMessage.info as UserMessage
65+
const now = Date.now()
66+
const messageId = generateUniqueId("msg")
67+
const partId = generateUniqueId("prt")
68+
69+
return {
70+
info: {
71+
id: messageId,
72+
sessionID: userInfo.sessionID,
73+
role: "assistant" as const,
74+
agent: userInfo.agent || "code",
75+
parentID: userInfo.id,
76+
modelID: userInfo.model.modelID,
77+
providerID: userInfo.model.providerID,
78+
mode: "default",
79+
path: {
80+
cwd: "/",
81+
root: "/",
82+
},
83+
time: { created: now, completed: now },
84+
cost: 0,
85+
tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
86+
...(variant !== undefined && { variant }),
87+
},
88+
parts: [
89+
{
90+
id: partId,
91+
sessionID: userInfo.sessionID,
92+
messageID: messageId,
93+
type: "text" as const,
94+
text: content,
95+
},
96+
],
97+
}
98+
}
99+
48100
export const createSyntheticTextPart = (baseMessage: WithParts, content: string) => {
49101
const userInfo = baseMessage.info as UserMessage
50102
const partId = generateUniqueId("prt")

0 commit comments

Comments
 (0)