🐛 fix(playground): improve multimodal content handling and error resilience

Fix TypeError when processing multimodal messages containing both text and images.
The error "textContent.text.trim is not a function" occurred when textContent
was null or textContent.text was not a string type.

Changes:
- Add comprehensive type checking for textContent.text access
- Implement getTextContent() utility function for unified content extraction
- Enhance error handling to support multimodal content display
- Fix message copy functionality to handle array-format content
- Improve message reset functionality to extract text content for retry
- Add user-friendly warnings when copying messages without text content

Technical improvements:
- Validate textContent existence and text property type before calling trim()
- Extract text content from multimodal messages for operations like copy/retry
- Maintain backward compatibility with string-format content
- Preserve all existing functionality while adding robust error handling

Fixes issues with:
- Image + text message processing
- Message copying from multimodal content
- Message retry with image attachments
- Error display for complex message formats

This ensures the playground component handles multimodal content gracefully
without breaking existing text-only message functionality.
This commit is contained in:
Apple\Apple
2025-05-30 19:32:49 +08:00
parent c5ed0753a6
commit 9c5ab755c1
2 changed files with 71 additions and 13 deletions

View File

@@ -712,18 +712,41 @@ const Playground = () => {
const handleMessageCopy = useCallback((message) => {
if (!message.content) return;
let textToCopy;
if (Array.isArray(message.content)) {
const textContent = message.content.find(item => item.type === 'text');
if (textContent && textContent.text && typeof textContent.text === 'string') {
textToCopy = textContent.text;
} else {
Toast.warning({
content: t('此消息没有可复制的文本内容'),
duration: 2,
});
return;
}
} else if (typeof message.content === 'string') {
textToCopy = message.content;
} else {
Toast.warning({
content: t('无法复制此类型的消息内容'),
duration: 2,
});
return;
}
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(message.content).then(() => {
navigator.clipboard.writeText(textToCopy).then(() => {
Toast.success({
content: t('消息已复制到剪贴板'),
duration: 2,
});
}).catch(err => {
console.error('Clipboard API 复制失败:', err);
fallbackCopyToClipboard(message.content);
fallbackCopyToClipboard(textToCopy);
});
} else {
fallbackCopyToClipboard(message.content);
fallbackCopyToClipboard(textToCopy);
}
}, [t]);
@@ -790,7 +813,14 @@ const Playground = () => {
if (targetMessage.role === 'user') {
const newMessages = prevMessages.slice(0, messageIndex);
setTimeout(() => {
onMessageSend(targetMessage.content);
let contentToSend;
if (Array.isArray(targetMessage.content)) {
const textContent = targetMessage.content.find(item => item.type === 'text');
contentToSend = textContent && textContent.text ? textContent.text : '';
} else {
contentToSend = targetMessage.content;
}
onMessageSend(contentToSend);
}, 100);
return newMessages;
} else if (targetMessage.role === 'assistant') {
@@ -802,7 +832,14 @@ const Playground = () => {
const userMessage = prevMessages[userMessageIndex];
const newMessages = prevMessages.slice(0, userMessageIndex);
setTimeout(() => {
onMessageSend(userMessage.content);
let contentToSend;
if (Array.isArray(userMessage.content)) {
const textContent = userMessage.content.find(item => item.type === 'text');
contentToSend = textContent && textContent.text ? textContent.text : '';
} else {
contentToSend = userMessage.content;
}
onMessageSend(contentToSend);
}, 100);
return newMessages;
}