Merge branch 'fix-empty-message-content'

This commit is contained in:
YeonGyu-Kim
2025-12-16 21:02:38 +09:00
2 changed files with 74 additions and 3 deletions

View File

@@ -4,12 +4,14 @@ import {
findEmptyMessages, findEmptyMessages,
findEmptyMessageByIndex, findEmptyMessageByIndex,
findMessageByIndexNeedingThinking, findMessageByIndexNeedingThinking,
findMessagesWithEmptyTextParts,
findMessagesWithOrphanThinking, findMessagesWithOrphanThinking,
findMessagesWithThinkingBlocks, findMessagesWithThinkingBlocks,
findMessagesWithThinkingOnly, findMessagesWithThinkingOnly,
injectTextPart, injectTextPart,
prependThinkingPart, prependThinkingPart,
readParts, readParts,
replaceEmptyTextParts,
stripThinkingParts, stripThinkingParts,
} from "./storage" } from "./storage"
import type { MessageData } from "./types" import type { MessageData } from "./types"
@@ -222,28 +224,48 @@ async function recoverEmptyContentMessage(
): Promise<boolean> { ): Promise<boolean> {
const targetIndex = extractMessageIndex(error) const targetIndex = extractMessageIndex(error)
const failedID = failedAssistantMsg.info?.id const failedID = failedAssistantMsg.info?.id
let anySuccess = false
const messagesWithEmptyText = findMessagesWithEmptyTextParts(sessionID)
for (const messageID of messagesWithEmptyText) {
if (replaceEmptyTextParts(messageID, PLACEHOLDER_TEXT)) {
anySuccess = true
}
}
const thinkingOnlyIDs = findMessagesWithThinkingOnly(sessionID) const thinkingOnlyIDs = findMessagesWithThinkingOnly(sessionID)
for (const messageID of thinkingOnlyIDs) { for (const messageID of thinkingOnlyIDs) {
injectTextPart(sessionID, messageID, PLACEHOLDER_TEXT) if (injectTextPart(sessionID, messageID, PLACEHOLDER_TEXT)) {
anySuccess = true
}
} }
if (targetIndex !== null) { if (targetIndex !== null) {
const targetMessageID = findEmptyMessageByIndex(sessionID, targetIndex) const targetMessageID = findEmptyMessageByIndex(sessionID, targetIndex)
if (targetMessageID) { if (targetMessageID) {
return injectTextPart(sessionID, targetMessageID, PLACEHOLDER_TEXT) if (replaceEmptyTextParts(targetMessageID, PLACEHOLDER_TEXT)) {
return true
}
if (injectTextPart(sessionID, targetMessageID, PLACEHOLDER_TEXT)) {
return true
}
} }
} }
if (failedID) { if (failedID) {
if (replaceEmptyTextParts(failedID, PLACEHOLDER_TEXT)) {
return true
}
if (injectTextPart(sessionID, failedID, PLACEHOLDER_TEXT)) { if (injectTextPart(sessionID, failedID, PLACEHOLDER_TEXT)) {
return true return true
} }
} }
const emptyMessageIDs = findEmptyMessages(sessionID) const emptyMessageIDs = findEmptyMessages(sessionID)
let anySuccess = thinkingOnlyIDs.length > 0
for (const messageID of emptyMessageIDs) { for (const messageID of emptyMessageIDs) {
if (replaceEmptyTextParts(messageID, PLACEHOLDER_TEXT)) {
anySuccess = true
}
if (injectTextPart(sessionID, messageID, PLACEHOLDER_TEXT)) { if (injectTextPart(sessionID, messageID, PLACEHOLDER_TEXT)) {
anySuccess = true anySuccess = true
} }

View File

@@ -271,6 +271,55 @@ export function stripThinkingParts(messageID: string): boolean {
return anyRemoved return anyRemoved
} }
export function replaceEmptyTextParts(messageID: string, replacementText: string): boolean {
const partDir = join(PART_STORAGE, messageID)
if (!existsSync(partDir)) return false
let anyReplaced = false
for (const file of readdirSync(partDir)) {
if (!file.endsWith(".json")) continue
try {
const filePath = join(partDir, file)
const content = readFileSync(filePath, "utf-8")
const part = JSON.parse(content) as StoredPart
if (part.type === "text") {
const textPart = part as StoredTextPart
if (!textPart.text?.trim()) {
textPart.text = replacementText
textPart.synthetic = true
writeFileSync(filePath, JSON.stringify(textPart, null, 2))
anyReplaced = true
}
}
} catch {
continue
}
}
return anyReplaced
}
export function findMessagesWithEmptyTextParts(sessionID: string): string[] {
const messages = readMessages(sessionID)
const result: string[] = []
for (const msg of messages) {
const parts = readParts(msg.id)
const hasEmptyTextPart = parts.some((p) => {
if (p.type !== "text") return false
const textPart = p as StoredTextPart
return !textPart.text?.trim()
})
if (hasEmptyTextPart) {
result.push(msg.id)
}
}
return result
}
export function findMessageByIndexNeedingThinking(sessionID: string, targetIndex: number): string | null { export function findMessageByIndexNeedingThinking(sessionID: string, targetIndex: number): string | null {
const messages = readMessages(sessionID) const messages = readMessages(sessionID)