Refine chat tool status dedupe (#786)

Co-authored-by: zuolingxuan <zuolingxuan@bytedance.com>
This commit is contained in:
Lingxuan Zuo
2026-04-08 15:05:27 +08:00
committed by GitHub
Unverified
parent 32d14b8cf9
commit 25b13ab912
11 changed files with 328 additions and 78 deletions

View File

@@ -75,6 +75,13 @@ function upsertImageCacheEntry(filePath: string, file: Omit<AttachedFileMeta, 'f
saveImageCache(_imageCache);
}
function withAttachedFileSource(
file: AttachedFileMeta,
source: AttachedFileMeta['source'],
): AttachedFileMeta {
return file.source ? file : { ...file, source };
}
/** Extract plain text from message content (string or content blocks) */
function getMessageText(content: unknown): string {
if (typeof content === 'string') return content;
@@ -228,11 +235,14 @@ function extractImagesAsAttachedFiles(content: unknown): AttachedFileMeta[] {
/**
* Build an AttachedFileMeta entry for a file ref, using cache if available.
*/
function makeAttachedFile(ref: { filePath: string; mimeType: string }): AttachedFileMeta {
function makeAttachedFile(
ref: { filePath: string; mimeType: string },
source: AttachedFileMeta['source'] = 'message-ref',
): AttachedFileMeta {
const cached = _imageCache.get(ref.filePath);
if (cached) return { ...cached, filePath: ref.filePath };
if (cached) return { ...cached, filePath: ref.filePath, source };
const fileName = ref.filePath.split(/[\\/]/).pop() || 'file';
return { fileName, mimeType: ref.mimeType, fileSize: 0, preview: null, filePath: ref.filePath };
return { fileName, mimeType: ref.mimeType, fileSize: 0, preview: null, filePath: ref.filePath, source };
}
/**
@@ -345,7 +355,7 @@ function enrichWithToolResultFiles(messages: RawMessage[]): RawMessage[] {
}
}
}
pending.push(...imageFiles);
pending.push(...imageFiles.map((file) => withAttachedFileSource(file, 'tool-result')));
// 2. [media attached: ...] patterns in tool result text output
const text = getMessageText(msg.content);
@@ -353,12 +363,12 @@ function enrichWithToolResultFiles(messages: RawMessage[]): RawMessage[] {
const mediaRefs = extractMediaRefs(text);
const mediaRefPaths = new Set(mediaRefs.map(r => r.filePath));
for (const ref of mediaRefs) {
pending.push(makeAttachedFile(ref));
pending.push(makeAttachedFile(ref, 'tool-result'));
}
// 3. Raw file paths in tool result text (documents, audio, video, etc.)
for (const ref of extractRawFilePaths(text)) {
if (!mediaRefPaths.has(ref.filePath)) {
pending.push(makeAttachedFile(ref));
pending.push(makeAttachedFile(ref, 'tool-result'));
}
}
}
@@ -435,9 +445,9 @@ function enrichWithCachedImages(messages: RawMessage[]): RawMessage[] {
const files: AttachedFileMeta[] = allRefs.map(ref => {
const cached = _imageCache.get(ref.filePath);
if (cached) return { ...cached, filePath: ref.filePath };
if (cached) return { ...cached, filePath: ref.filePath, source: 'message-ref' };
const fileName = ref.filePath.split(/[\\/]/).pop() || 'file';
return { fileName, mimeType: ref.mimeType, fileSize: 0, preview: null, filePath: ref.filePath };
return { fileName, mimeType: ref.mimeType, fileSize: 0, preview: null, filePath: ref.filePath, source: 'message-ref' };
});
return { ...msg, _attachedFiles: files };
});

View File

@@ -86,9 +86,8 @@ export function handleRuntimeEventState(
: undefined;
// Mirror enrichWithToolResultFiles: collect images + file refs for next assistant msg
const toolFiles: AttachedFileMeta[] = [
...extractImagesAsAttachedFiles(finalMsg.content),
];
const toolFiles: AttachedFileMeta[] = extractImagesAsAttachedFiles(finalMsg.content)
.map((file) => (file.source ? file : { ...file, source: 'tool-result' }));
if (matchedPath) {
for (const f of toolFiles) {
if (!f.filePath) {
@@ -101,9 +100,9 @@ export function handleRuntimeEventState(
if (text) {
const mediaRefs = extractMediaRefs(text);
const mediaRefPaths = new Set(mediaRefs.map(r => r.filePath));
for (const ref of mediaRefs) toolFiles.push(makeAttachedFile(ref));
for (const ref of mediaRefs) toolFiles.push(makeAttachedFile(ref, 'tool-result'));
for (const ref of extractRawFilePaths(text)) {
if (!mediaRefPaths.has(ref.filePath)) toolFiles.push(makeAttachedFile(ref));
if (!mediaRefPaths.has(ref.filePath)) toolFiles.push(makeAttachedFile(ref, 'tool-result'));
}
}
set((s) => {

View File

@@ -94,6 +94,7 @@ export function createRuntimeSendActions(set: ChatSet, get: ChatGet): Pick<Runti
fileSize: a.fileSize,
preview: a.preview,
filePath: a.stagedPath,
source: 'user-upload',
})),
};
set((s) => ({

View File

@@ -5,6 +5,7 @@ export interface AttachedFileMeta {
fileSize: number;
preview: string | null;
filePath?: string;
source?: 'user-upload' | 'tool-result' | 'message-ref';
}
/** Raw message from OpenClaw chat.history */