Update filtering out thought parts from getHistoryTurns

This commit is contained in:
davidapierce
2026-07-22 21:48:27 +00:00
parent c776c665b0
commit 0c5eae4d35
2 changed files with 96 additions and 10 deletions
+56
View File
@@ -2282,6 +2282,62 @@ describe('GeminiChat', () => {
text: 'actual conversational response',
});
});
it('should completely filter out thought parts from getHistoryTurns when context management is disabled but model is gemini-2/modern', () => {
vi.mocked(mockConfig.isContextManagementEnabled).mockReturnValue(false);
vi.mocked(mockConfig.getModel).mockReturnValue('gemini-2.5-pro');
chat.setHistory([
{
role: 'user',
parts: [{ text: 'hello' }],
},
{
role: 'model',
parts: [
{ text: 'internal monologue', thought: true } as unknown as Part,
{ text: 'actual conversational response' },
],
},
]);
const turns = chat.getHistoryTurns(true);
expect(turns).toHaveLength(2);
const modelTurn = turns[1];
expect(modelTurn.content.parts).toHaveLength(1);
expect(modelTurn.content.parts![0]).toEqual({
text: 'actual conversational response',
});
});
it('should completely filter out thought parts from getHistoryTurns when model supports modern features', () => {
vi.mocked(mockConfig.isContextManagementEnabled).mockReturnValue(false);
vi.mocked(mockConfig.getModel).mockReturnValue('gemini-3.1-pro-preview');
chat.setHistory([
{
role: 'user',
parts: [{ text: 'hello' }],
},
{
role: 'model',
parts: [
{ text: 'internal monologue', thought: true } as unknown as Part,
{ text: 'actual conversational response' },
],
},
]);
const turns = chat.getHistoryTurns(true);
expect(turns).toHaveLength(2);
const modelTurn = turns[1];
expect(modelTurn.content.parts).toHaveLength(1);
expect(modelTurn.content.parts![0]).toEqual({
text: 'actual conversational response',
});
});
});
describe('ensureActiveLoopHasThoughtSignatures', () => {
+40 -10
View File
@@ -30,7 +30,11 @@ import {
getRetryErrorType,
} from '../utils/retry.js';
import type { ValidationRequiredError } from '../utils/googleQuotaErrors.js';
import { resolveModel, supportsModernFeatures } from '../config/models.js';
import {
resolveModel,
supportsModernFeatures,
isGemini2Model,
} from '../config/models.js';
import { hasCycleInSchema } from '../tools/tools.js';
import type { StructuredError } from './turn.js';
import type { CompletedToolCall } from '../scheduler/types.js';
@@ -766,9 +770,10 @@ export class GeminiChat {
abortSignal,
};
let contentsToUse: Content[] = supportsModernFeatures(modelToUse)
? [...contentsForPreviewModel]
: [...requestContents];
let contentsToUse: Content[] =
supportsModernFeatures(modelToUse) || isGemini2Model(modelToUse)
? [...contentsForPreviewModel]
: [...requestContents];
const hookSystem = this.context.config.getHookSystem();
if (hookSystem) {
@@ -810,9 +815,10 @@ export class GeminiChat {
);
lastModelToUse = modelToUse;
// Re-evaluate contentsToUse based on the new model's feature support
contentsToUse = supportsModernFeatures(modelToUse)
? [...contentsForPreviewModel]
: [...requestContents];
contentsToUse =
supportsModernFeatures(modelToUse) || isGemini2Model(modelToUse)
? [...contentsForPreviewModel]
: [...requestContents];
}
if (beforeModelResult.modifiedConfig) {
Object.assign(config, beforeModelResult.modifiedConfig);
@@ -956,9 +962,16 @@ export class GeminiChat {
? extractCuratedHistory(this.agentHistory.get())
: [...this.agentHistory.get()];
return this.context.config.isContextManagementEnabled()
? scrubHistory(history)
: history;
if (this.context.config.isContextManagementEnabled()) {
return scrubHistory(history);
}
const model = this.context.config.getModel();
if (isGemini2Model(model) || supportsModernFeatures(model)) {
return stripThoughts(history);
}
return history;
}
/**
@@ -1503,3 +1516,20 @@ export function coalesceConsecutiveRoles(
}
return result;
}
export function stripThoughts(history: HistoryTurn[]): HistoryTurn[] {
return history.map((turn) => {
if (!turn.content.parts) return turn;
const hasThought = turn.content.parts.some((p) => p && p.thought);
if (!hasThought) return turn;
const nonThoughtParts = turn.content.parts.filter((p) => p && !p.thought);
return {
id: turn.id,
content: {
...turn.content,
parts: nonThoughtParts,
},
};
});
}