mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-08-09 00:16:57 -07:00
fix(core): mitigate infinite ReAct loops and prompt injection loops (#28429)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
This commit is contained in:
@@ -175,6 +175,93 @@ describe('LoopDetectionService', () => {
|
||||
}
|
||||
expect(loggers.logLoopDetected).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should detect an alternating tool call loop (cycle of length 2)', () => {
|
||||
const eventA = createToolCallRequestEvent('read_file', {
|
||||
file_path: 'loop_a.txt',
|
||||
});
|
||||
const eventB = createToolCallRequestEvent('read_file', {
|
||||
file_path: 'loop_b.txt',
|
||||
});
|
||||
|
||||
let loopCount = 0;
|
||||
for (let i = 0; i < 15; i++) {
|
||||
const currentEvent = i % 2 === 0 ? eventA : eventB;
|
||||
const result = service.addAndCheck(currentEvent);
|
||||
if (result.count > 0) {
|
||||
loopCount = result.count;
|
||||
}
|
||||
}
|
||||
|
||||
expect(loopCount).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should detect a cyclic tool call loop of length 3', () => {
|
||||
const eventA = createToolCallRequestEvent('read_file', {
|
||||
file_path: 'loop_a.txt',
|
||||
});
|
||||
const eventB = createToolCallRequestEvent('read_file', {
|
||||
file_path: 'loop_b.txt',
|
||||
});
|
||||
const eventC = createToolCallRequestEvent('read_file', {
|
||||
file_path: 'loop_c.txt',
|
||||
});
|
||||
|
||||
let loopCount = 0;
|
||||
const sequence = [eventA, eventB, eventC];
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const result = service.addAndCheck(sequence[i % 3]);
|
||||
if (result.count > 0) {
|
||||
loopCount = result.count;
|
||||
}
|
||||
}
|
||||
|
||||
expect(loopCount).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should detect a cyclic tool call loop of length 5', () => {
|
||||
const e1 = createToolCallRequestEvent('t', { id: 1 });
|
||||
const e2 = createToolCallRequestEvent('t', { id: 2 });
|
||||
const e3 = createToolCallRequestEvent('t', { id: 3 });
|
||||
const e4 = createToolCallRequestEvent('t', { id: 4 });
|
||||
const e5 = createToolCallRequestEvent('t', { id: 5 });
|
||||
|
||||
let loopCount = 0;
|
||||
const sequence = [e1, e2, e3, e4, e5];
|
||||
for (let i = 0; i < 30; i++) {
|
||||
const result = service.addAndCheck(sequence[i % 5]);
|
||||
if (result.count > 0) {
|
||||
loopCount = result.count;
|
||||
}
|
||||
}
|
||||
|
||||
expect(loopCount).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should not detect loops for non-looping alternating sequences that vary', () => {
|
||||
const eventA = createToolCallRequestEvent('read_file', {
|
||||
file_path: 'loop_a.txt',
|
||||
});
|
||||
const eventB = createToolCallRequestEvent('read_file', {
|
||||
file_path: 'loop_b.txt',
|
||||
});
|
||||
const eventC = createToolCallRequestEvent('read_file', {
|
||||
file_path: 'loop_c.txt',
|
||||
});
|
||||
|
||||
// Run some alternating calls, then break the pattern with eventC
|
||||
for (let i = 0; i < 4; i++) {
|
||||
expect(service.addAndCheck(i % 2 === 0 ? eventA : eventB).count).toBe(
|
||||
0,
|
||||
);
|
||||
}
|
||||
expect(service.addAndCheck(eventC).count).toBe(0);
|
||||
for (let i = 0; i < 4; i++) {
|
||||
expect(service.addAndCheck(i % 2 === 0 ? eventA : eventB).count).toBe(
|
||||
0,
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('Content Loop Detection', () => {
|
||||
|
||||
Reference in New Issue
Block a user