summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/core/src/core/geminiChat.test.ts74
-rw-r--r--packages/core/src/core/geminiChat.ts17
2 files changed, 91 insertions, 0 deletions
diff --git a/packages/core/src/core/geminiChat.test.ts b/packages/core/src/core/geminiChat.test.ts
index 8d434dd4..3a6fb10c 100644
--- a/packages/core/src/core/geminiChat.test.ts
+++ b/packages/core/src/core/geminiChat.test.ts
@@ -298,5 +298,79 @@ describe('GeminiChat', () => {
expect(history[0]).toEqual(userInput);
expect(history[1]).toEqual(modelOutput[0]);
});
+
+ it('should skip "thought" content from modelOutput', () => {
+ const modelOutputWithThought: Content[] = [
+ { role: 'model', parts: [{ thought: true }, { text: 'Visible text' }] },
+ { role: 'model', parts: [{ text: 'Another visible text' }] },
+ ];
+ // @ts-expect-error Accessing private method for testing purposes
+ chat.recordHistory(userInput, modelOutputWithThought);
+ const history = chat.getHistory();
+ expect(history.length).toBe(2); // User input + consolidated model output
+ expect(history[0]).toEqual(userInput);
+ expect(history[1].role).toBe('model');
+ // The 'thought' part is skipped, 'Another visible text' becomes the first part.
+ expect(history[1].parts).toEqual([{ text: 'Another visible text' }]);
+ });
+
+ it('should skip "thought" content even if it is the only content', () => {
+ const modelOutputOnlyThought: Content[] = [
+ { role: 'model', parts: [{ thought: true }] },
+ ];
+ // @ts-expect-error Accessing private method for testing purposes
+ chat.recordHistory(userInput, modelOutputOnlyThought);
+ const history = chat.getHistory();
+ expect(history.length).toBe(1); // User input + default empty model part
+ expect(history[0]).toEqual(userInput);
+ });
+
+ it('should correctly consolidate text parts when a thought part is in between', () => {
+ const modelOutputMixed: Content[] = [
+ { role: 'model', parts: [{ text: 'Part 1.' }] },
+ {
+ role: 'model',
+ parts: [{ thought: true }, { text: 'Should be skipped' }],
+ },
+ { role: 'model', parts: [{ text: 'Part 2.' }] },
+ ];
+ // @ts-expect-error Accessing private method for testing purposes
+ chat.recordHistory(userInput, modelOutputMixed);
+ const history = chat.getHistory();
+ expect(history.length).toBe(2);
+ expect(history[0]).toEqual(userInput);
+ expect(history[1].role).toBe('model');
+ expect(history[1].parts).toEqual([{ text: 'Part 1.Part 2.' }]);
+ });
+
+ it('should handle multiple thought parts correctly', () => {
+ const modelOutputMultipleThoughts: Content[] = [
+ { role: 'model', parts: [{ thought: true }] },
+ { role: 'model', parts: [{ text: 'Visible 1' }] },
+ { role: 'model', parts: [{ thought: true }] },
+ { role: 'model', parts: [{ text: 'Visible 2' }] },
+ ];
+ // @ts-expect-error Accessing private method for testing purposes
+ chat.recordHistory(userInput, modelOutputMultipleThoughts);
+ const history = chat.getHistory();
+ expect(history.length).toBe(2);
+ expect(history[0]).toEqual(userInput);
+ expect(history[1].role).toBe('model');
+ expect(history[1].parts).toEqual([{ text: 'Visible 1Visible 2' }]);
+ });
+
+ it('should handle thought part at the end of outputContents', () => {
+ const modelOutputThoughtAtEnd: Content[] = [
+ { role: 'model', parts: [{ text: 'Visible text' }] },
+ { role: 'model', parts: [{ thought: true }] },
+ ];
+ // @ts-expect-error Accessing private method for testing purposes
+ chat.recordHistory(userInput, modelOutputThoughtAtEnd);
+ const history = chat.getHistory();
+ expect(history.length).toBe(2);
+ expect(history[0]).toEqual(userInput);
+ expect(history[1].role).toBe('model');
+ expect(history[1].parts).toEqual([{ text: 'Visible text' }]);
+ });
});
});
diff --git a/packages/core/src/core/geminiChat.ts b/packages/core/src/core/geminiChat.ts
index 25ea6e52..b4844499 100644
--- a/packages/core/src/core/geminiChat.ts
+++ b/packages/core/src/core/geminiChat.ts
@@ -342,6 +342,10 @@ export class GeminiChat {
// Consolidate adjacent model roles in outputContents
const consolidatedOutputContents: Content[] = [];
for (const content of outputContents) {
+ if (this.isThoughtContent(content)) {
+ continue;
+ }
+
const lastContent =
consolidatedOutputContents[consolidatedOutputContents.length - 1];
if (this.isTextContent(lastContent) && this.isTextContent(content)) {
@@ -394,4 +398,17 @@ export class GeminiChat {
content.parts[0].text !== ''
);
}
+
+ private isThoughtContent(
+ content: Content | undefined,
+ ): content is Content & { parts: [{ thought: boolean }, ...Part[]] } {
+ return !!(
+ content &&
+ content.role === 'model' &&
+ content.parts &&
+ content.parts.length > 0 &&
+ typeof content.parts[0].thought === 'boolean' &&
+ content.parts[0].thought === true
+ );
+ }
}