mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-11 14:40:52 -07:00
chore: add removal of function call if response causes 404
This commit is contained in:
@@ -1344,6 +1344,60 @@ describe('GeminiChat', () => {
|
||||
).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should remove function response AND preceding function call on 400 error', async () => {
|
||||
// Set up history with a user message and model function call
|
||||
const initialUserMessage: Content = {
|
||||
role: 'user',
|
||||
parts: [{ text: 'Call a tool for me' }],
|
||||
};
|
||||
const modelFunctionCall: Content = {
|
||||
role: 'model',
|
||||
parts: [{ functionCall: { name: 'test_tool', args: {} } }],
|
||||
};
|
||||
chat.addHistory(initialUserMessage);
|
||||
chat.addHistory(modelFunctionCall);
|
||||
|
||||
// Verify initial history state
|
||||
expect(chat.getHistory().length).toBe(2);
|
||||
|
||||
const error400 = new ApiError({ message: 'Bad Request', status: 400 });
|
||||
vi.mocked(mockContentGenerator.generateContentStream).mockRejectedValue(
|
||||
error400,
|
||||
);
|
||||
|
||||
// Send a function response that will fail with 400
|
||||
const functionResponse = [
|
||||
{
|
||||
functionResponse: {
|
||||
name: 'test_tool',
|
||||
response: { invalid: 'data' },
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const stream = await chat.sendMessageStream(
|
||||
{ model: 'gemini-2.5-flash' },
|
||||
functionResponse,
|
||||
'prompt-id-400-fn',
|
||||
new AbortController().signal,
|
||||
);
|
||||
|
||||
await expect(
|
||||
(async () => {
|
||||
for await (const _ of stream) {
|
||||
/* consume stream */
|
||||
}
|
||||
})(),
|
||||
).rejects.toThrow(error400);
|
||||
|
||||
// History should only contain the initial user message.
|
||||
// Both the function response AND the model's function call should be removed
|
||||
// to avoid a dangling function call state.
|
||||
const history = chat.getHistory();
|
||||
expect(history.length).toBe(1);
|
||||
expect(history[0]).toEqual(initialUserMessage);
|
||||
});
|
||||
|
||||
it('should retry on 429 Rate Limit errors', async () => {
|
||||
const error429 = new ApiError({ message: 'Rate Limited', status: 429 });
|
||||
|
||||
|
||||
@@ -41,7 +41,10 @@ import {
|
||||
ContentRetryFailureEvent,
|
||||
} from '../telemetry/types.js';
|
||||
import { handleFallback } from '../fallback/handler.js';
|
||||
import { isFunctionResponse } from '../utils/messageInspectors.js';
|
||||
import {
|
||||
isFunctionResponse,
|
||||
isFunctionCall,
|
||||
} from '../utils/messageInspectors.js';
|
||||
import { partListUnionToString } from './geminiRequest.js';
|
||||
import type { ModelConfigKey } from '../services/modelConfigService.js';
|
||||
import { estimateTokenCountSync } from '../utils/tokenCalculation.js';
|
||||
@@ -387,8 +390,7 @@ export class GeminiChat {
|
||||
);
|
||||
|
||||
if (isConnectionPhase && !isRetryable) {
|
||||
// Remove failed user content to not break subsequent requests
|
||||
this.history.pop();
|
||||
this.popFailedUserContent();
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -432,8 +434,7 @@ export class GeminiChat {
|
||||
new ContentRetryFailureEvent(maxAttempts, lastError.type, model),
|
||||
);
|
||||
}
|
||||
// Remove failed user content so it doesn't break subsequent requests
|
||||
this.history.pop();
|
||||
this.popFailedUserContent();
|
||||
throw lastError;
|
||||
}
|
||||
} finally {
|
||||
@@ -678,6 +679,21 @@ export class GeminiChat {
|
||||
this.history = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes failed user content from history. If the content was a function
|
||||
* response, also removes the preceding model function call to keep
|
||||
* history consistent (avoids dangling function call state).
|
||||
*/
|
||||
private popFailedUserContent(): void {
|
||||
const popped = this.history.pop();
|
||||
if (popped && isFunctionResponse(popped)) {
|
||||
const prev = this.history[this.history.length - 1];
|
||||
if (prev && isFunctionCall(prev)) {
|
||||
this.history.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new entry to the chat history.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user