fix cross-protocol conversation file compatibility
This commit is contained in:
@@ -23,6 +23,7 @@ import {
|
||||
createLoadingAssistantMessage,
|
||||
createMessage,
|
||||
createConversationUserMessage,
|
||||
extractConversationUploadedFilesFromMessage,
|
||||
filterModelTesterModelNames,
|
||||
finalizeIncompleteMessage,
|
||||
findLastLoadingAssistantIndex,
|
||||
@@ -898,20 +899,6 @@ export default function ModelTester() {
|
||||
setConversationFiles((prev) => prev.filter((item) => item.localId !== localId));
|
||||
}, [sending]);
|
||||
|
||||
const extractUploadedFilesFromMessage = useCallback((message: ChatMessage): ConversationUploadedFile[] => {
|
||||
const parts = Array.isArray(message.parts) ? message.parts : [];
|
||||
return parts.flatMap((part) => {
|
||||
if (part.type !== 'input_file') return [];
|
||||
const fileId = typeof part.fileId === 'string' ? part.fileId.trim() : '';
|
||||
if (!fileId) return [];
|
||||
return [{
|
||||
fileId,
|
||||
filename: typeof part.filename === 'string' && part.filename.trim() ? part.filename.trim() : null,
|
||||
mimeType: typeof part.mimeType === 'string' && part.mimeType.trim() ? part.mimeType.trim() : null,
|
||||
}];
|
||||
});
|
||||
}, []);
|
||||
|
||||
const uploadConversationFiles = useCallback(async (): Promise<ConversationUploadedFile[]> => {
|
||||
if (conversationFiles.length <= 0) return [];
|
||||
|
||||
@@ -2047,11 +2034,11 @@ export default function ModelTester() {
|
||||
|
||||
const base = messages.slice(0, userIndex);
|
||||
const prompt = messages[userIndex].content;
|
||||
const files = extractUploadedFilesFromMessage(messages[userIndex]);
|
||||
const files = extractConversationUploadedFilesFromMessage(messages[userIndex]);
|
||||
setEditingMessageId(null);
|
||||
setEditValue('');
|
||||
void sendWithPrompt(prompt, base, files);
|
||||
}, [extractUploadedFilesFromMessage, messages, pendingJobId, sendWithPrompt, sending]);
|
||||
}, [messages, pendingJobId, sendWithPrompt, sending]);
|
||||
|
||||
const startEditMessage = useCallback((target: ChatMessage) => {
|
||||
if (sending) return;
|
||||
@@ -2085,9 +2072,9 @@ export default function ModelTester() {
|
||||
|
||||
if (retry && target.role === 'user') {
|
||||
const base = updated.slice(0, targetIndex);
|
||||
void sendWithPrompt(nextContent, base, extractUploadedFilesFromMessage(target));
|
||||
void sendWithPrompt(nextContent, base, extractConversationUploadedFilesFromMessage(target));
|
||||
}
|
||||
}, [cancelEditMessage, editValue, editingMessageId, extractUploadedFilesFromMessage, messages, sendWithPrompt]);
|
||||
}, [cancelEditMessage, editValue, editingMessageId, messages, sendWithPrompt]);
|
||||
|
||||
const syncMessageToBody = useCallback(() => {
|
||||
const nextBody = syncMessagesToCustomRequestBody(customRequestBody, messages, inputs);
|
||||
|
||||
@@ -16,16 +16,16 @@ describe('resolveConversationFileCapability', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('marks Claude and Gemini as inline-only document transports', () => {
|
||||
it('marks Claude and Gemini picker flows as inline-only document transports', () => {
|
||||
expect(resolveConversationFileCapability('claude')).toEqual({
|
||||
supported: true,
|
||||
documentMode: 'inline_only',
|
||||
reason: '当前协议会以内联文档方式发送会话附件。',
|
||||
reason: '当前界面的会话附件会以内联文档方式发送。',
|
||||
});
|
||||
expect(resolveConversationFileCapability('gemini')).toEqual({
|
||||
supported: true,
|
||||
documentMode: 'inline_only',
|
||||
reason: '当前协议会以内联文档方式发送会话附件。',
|
||||
reason: '当前界面的会话附件会以内联文档方式发送。',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,7 +21,7 @@ export function resolveConversationFileCapability(
|
||||
return {
|
||||
supported: true,
|
||||
documentMode: 'inline_only',
|
||||
reason: '当前协议会以内联文档方式发送会话附件。',
|
||||
reason: '当前界面的会话附件会以内联文档方式发送。',
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
collectModelTesterModelNames,
|
||||
countConversationTurns,
|
||||
createConversationUserMessage,
|
||||
extractConversationUploadedFilesFromMessage,
|
||||
filterModelTesterModelNames,
|
||||
parseCustomRequestBody,
|
||||
parseModelTesterSession,
|
||||
@@ -222,6 +223,56 @@ describe('modelTesterSession', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('extracts inline conversation file data for retry flows', () => {
|
||||
const files = extractConversationUploadedFilesFromMessage({
|
||||
id: 'u1',
|
||||
role: 'user',
|
||||
content: '请总结附件',
|
||||
createAt: 1,
|
||||
parts: [
|
||||
{
|
||||
type: 'input_file',
|
||||
filename: 'brief.pdf',
|
||||
mimeType: 'application/pdf',
|
||||
data: 'data:application/pdf;base64,JVBERi0xLjQK',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(files).toEqual([
|
||||
{
|
||||
filename: 'brief.pdf',
|
||||
mimeType: 'application/pdf',
|
||||
data: 'data:application/pdf;base64,JVBERi0xLjQK',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('extracts uploaded file references for retry flows', () => {
|
||||
const files = extractConversationUploadedFilesFromMessage({
|
||||
id: 'u2',
|
||||
role: 'user',
|
||||
content: '请继续分析',
|
||||
createAt: 2,
|
||||
parts: [
|
||||
{
|
||||
type: 'input_file',
|
||||
fileId: 'file-metapi-456',
|
||||
filename: 'appendix.txt',
|
||||
mimeType: 'text/plain',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(files).toEqual([
|
||||
{
|
||||
fileId: 'file-metapi-456',
|
||||
filename: 'appendix.txt',
|
||||
mimeType: 'text/plain',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('preserves file reference parts when building responses conversation payloads', () => {
|
||||
const payload = buildApiPayload(
|
||||
[{
|
||||
|
||||
@@ -901,6 +901,37 @@ export const createConversationUserMessage = (
|
||||
});
|
||||
};
|
||||
|
||||
export const extractConversationUploadedFilesFromMessage = (
|
||||
message: ChatMessage,
|
||||
): ConversationUploadedFile[] => {
|
||||
const parts = Array.isArray(message.parts) ? message.parts : [];
|
||||
return parts.flatMap((part) => {
|
||||
if (part.type !== 'input_file') return [];
|
||||
|
||||
const fileId = typeof part.fileId === 'string' && part.fileId.trim()
|
||||
? part.fileId.trim()
|
||||
: null;
|
||||
const filename = typeof part.filename === 'string' && part.filename.trim()
|
||||
? part.filename.trim()
|
||||
: null;
|
||||
const mimeType = typeof part.mimeType === 'string' && part.mimeType.trim()
|
||||
? part.mimeType.trim()
|
||||
: null;
|
||||
const data = typeof part.data === 'string' && part.data.trim()
|
||||
? part.data.trim()
|
||||
: null;
|
||||
|
||||
if (!fileId && !data) return [];
|
||||
|
||||
return [{
|
||||
...(fileId ? { fileId } : {}),
|
||||
...(filename ? { filename } : {}),
|
||||
...(mimeType ? { mimeType } : {}),
|
||||
...(data ? { data } : {}),
|
||||
}];
|
||||
});
|
||||
};
|
||||
|
||||
export const createLoadingAssistantMessage = (): ChatMessage =>
|
||||
createMessage('assistant', '', {
|
||||
status: MESSAGE_STATUS.LOADING,
|
||||
|
||||
Reference in New Issue
Block a user