fix(core): correctly consolidate multi-part text content (#6235)

Co-authored-by: Allen Hutchison <adh@google.com>
This commit is contained in:
Swapnaneel Patra
2025-08-22 22:42:10 +05:30
committed by GitHub
parent 9c1490e985
commit f61acf60f6

View File

@@ -536,10 +536,15 @@ export class GeminiChat {
} }
const lastContent = const lastContent =
consolidatedOutputContents[consolidatedOutputContents.length - 1]; consolidatedOutputContents[consolidatedOutputContents.length - 1];
if (this.isTextContent(lastContent) && this.isTextContent(content)) { if (
// If both current and last are text, combine their text into the lastContent's first part lastContent &&
// and append any other parts from the current content. this.isLastPartText(lastContent) &&
lastContent.parts[0].text += content.parts[0].text || ''; this.isFirstPartText(content)
) {
// If the last part of the previous content and the first part of the current content are text,
// combine their text and append any other parts from the current content.
lastContent.parts[lastContent.parts.length - 1].text +=
content.parts[0].text || '';
if (content.parts.length > 1) { if (content.parts.length > 1) {
lastContent.parts.push(...content.parts.slice(1)); lastContent.parts.push(...content.parts.slice(1));
} }
@@ -556,12 +561,13 @@ export class GeminiChat {
if ( if (
canMergeWithLastHistory && canMergeWithLastHistory &&
this.isTextContent(lastHistoryEntry) && lastHistoryEntry &&
this.isTextContent(consolidatedOutputContents[0]) this.isLastPartText(lastHistoryEntry) &&
this.isFirstPartText(consolidatedOutputContents[0])
) { ) {
// If both current and last are text, combine their text into the lastHistoryEntry's first part // If the last part of the last history entry and the first part of the current content are text,
// and append any other parts from the current content. // combine their text and append any other parts from the current content.
lastHistoryEntry.parts[0].text += lastHistoryEntry.parts[lastHistoryEntry.parts.length - 1].text +=
consolidatedOutputContents[0].parts[0].text || ''; consolidatedOutputContents[0].parts[0].text || '';
if (consolidatedOutputContents[0].parts.length > 1) { if (consolidatedOutputContents[0].parts.length > 1) {
lastHistoryEntry.parts.push( lastHistoryEntry.parts.push(
@@ -574,16 +580,48 @@ export class GeminiChat {
} }
} }
private isTextContent( private isFirstPartText(
content: Content | undefined, content: Content | undefined,
): content is Content & { parts: [{ text: string }, ...Part[]] } { ): content is Content & { parts: [{ text: string }, ...Part[]] } {
return !!( if (
content && !content ||
content.role === 'model' && content.role !== 'model' ||
content.parts && !content.parts ||
content.parts.length > 0 && content.parts.length === 0
typeof content.parts[0].text === 'string' && ) {
content.parts[0].text !== '' return false;
}
const firstPart = content.parts[0];
return (
typeof firstPart.text === 'string' &&
!('functionCall' in firstPart) &&
!('functionResponse' in firstPart) &&
!('inlineData' in firstPart) &&
!('fileData' in firstPart) &&
!('thought' in firstPart)
);
}
private isLastPartText(
content: Content | undefined,
): content is Content & { parts: [...Part[], { text: string }] } {
if (
!content ||
content.role !== 'model' ||
!content.parts ||
content.parts.length === 0
) {
return false;
}
const lastPart = content.parts[content.parts.length - 1];
return (
typeof lastPart.text === 'string' &&
lastPart.text !== '' &&
!('functionCall' in lastPart) &&
!('functionResponse' in lastPart) &&
!('inlineData' in lastPart) &&
!('fileData' in lastPart) &&
!('thought' in lastPart)
); );
} }