mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-13 13:22:35 -07:00
refactor(core): extract static concerns from CoreToolScheduler (#15589)
This commit is contained in:
@@ -14,14 +14,20 @@ import {
|
||||
getStructuredResponse,
|
||||
getStructuredResponseFromParts,
|
||||
getCitations,
|
||||
convertToFunctionResponse,
|
||||
} from './generateContentResponseUtilities.js';
|
||||
import type {
|
||||
GenerateContentResponse,
|
||||
Part,
|
||||
SafetyRating,
|
||||
CitationMetadata,
|
||||
PartListUnion,
|
||||
} from '@google/genai';
|
||||
import { FinishReason } from '@google/genai';
|
||||
import {
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
PREVIEW_GEMINI_MODEL,
|
||||
} from '../config/models.js';
|
||||
|
||||
const mockTextPart = (text: string): Part => ({ text });
|
||||
const mockFunctionCallPart = (
|
||||
@@ -72,6 +78,312 @@ const minimalMockResponse = (
|
||||
});
|
||||
|
||||
describe('generateContentResponseUtilities', () => {
|
||||
describe('convertToFunctionResponse', () => {
|
||||
const toolName = 'testTool';
|
||||
const callId = 'call1';
|
||||
|
||||
it('should handle simple string llmContent', () => {
|
||||
const llmContent = 'Simple text output';
|
||||
const result = convertToFunctionResponse(
|
||||
toolName,
|
||||
callId,
|
||||
llmContent,
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
);
|
||||
expect(result).toEqual([
|
||||
{
|
||||
functionResponse: {
|
||||
name: toolName,
|
||||
id: callId,
|
||||
response: { output: 'Simple text output' },
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle llmContent as a single Part with text', () => {
|
||||
const llmContent: Part = { text: 'Text from Part object' };
|
||||
const result = convertToFunctionResponse(
|
||||
toolName,
|
||||
callId,
|
||||
llmContent,
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
);
|
||||
expect(result).toEqual([
|
||||
{
|
||||
functionResponse: {
|
||||
name: toolName,
|
||||
id: callId,
|
||||
response: { output: 'Text from Part object' },
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle llmContent as a PartListUnion array with a single text Part', () => {
|
||||
const llmContent: PartListUnion = [{ text: 'Text from array' }];
|
||||
const result = convertToFunctionResponse(
|
||||
toolName,
|
||||
callId,
|
||||
llmContent,
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
);
|
||||
expect(result).toEqual([
|
||||
{
|
||||
functionResponse: {
|
||||
name: toolName,
|
||||
id: callId,
|
||||
response: { output: 'Text from array' },
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle llmContent as a PartListUnion array with multiple Parts', () => {
|
||||
const llmContent: PartListUnion = [{ text: 'part1' }, { text: 'part2' }];
|
||||
const result = convertToFunctionResponse(
|
||||
toolName,
|
||||
callId,
|
||||
llmContent,
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
);
|
||||
expect(result).toEqual([
|
||||
{
|
||||
functionResponse: {
|
||||
name: toolName,
|
||||
id: callId,
|
||||
response: { output: 'part1\npart2' },
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle llmContent with fileData for Gemini 3 model (should be siblings)', () => {
|
||||
const llmContent: Part = {
|
||||
fileData: { mimeType: 'application/pdf', fileUri: 'gs://...' },
|
||||
};
|
||||
const result = convertToFunctionResponse(
|
||||
toolName,
|
||||
callId,
|
||||
llmContent,
|
||||
PREVIEW_GEMINI_MODEL,
|
||||
);
|
||||
expect(result).toEqual([
|
||||
{
|
||||
functionResponse: {
|
||||
name: toolName,
|
||||
id: callId,
|
||||
response: { output: 'Binary content provided (1 item(s)).' },
|
||||
},
|
||||
},
|
||||
llmContent,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle llmContent with inlineData for Gemini 3 model (should be nested)', () => {
|
||||
const llmContent: Part = {
|
||||
inlineData: { mimeType: 'image/png', data: 'base64...' },
|
||||
};
|
||||
const result = convertToFunctionResponse(
|
||||
toolName,
|
||||
callId,
|
||||
llmContent,
|
||||
PREVIEW_GEMINI_MODEL,
|
||||
);
|
||||
expect(result).toEqual([
|
||||
{
|
||||
functionResponse: {
|
||||
name: toolName,
|
||||
id: callId,
|
||||
response: { output: 'Binary content provided (1 item(s)).' },
|
||||
parts: [llmContent],
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle llmContent with fileData for non-Gemini 3 models', () => {
|
||||
const llmContent: Part = {
|
||||
fileData: { mimeType: 'application/pdf', fileUri: 'gs://...' },
|
||||
};
|
||||
const result = convertToFunctionResponse(
|
||||
toolName,
|
||||
callId,
|
||||
llmContent,
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
);
|
||||
expect(result).toEqual([
|
||||
{
|
||||
functionResponse: {
|
||||
name: toolName,
|
||||
id: callId,
|
||||
response: { output: 'Binary content provided (1 item(s)).' },
|
||||
},
|
||||
},
|
||||
llmContent,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should preserve existing functionResponse metadata', () => {
|
||||
const innerId = 'inner-call-id';
|
||||
const innerName = 'inner-tool-name';
|
||||
const responseMetadata = {
|
||||
flags: ['flag1'],
|
||||
isError: false,
|
||||
customData: { key: 'value' },
|
||||
};
|
||||
const input: Part = {
|
||||
functionResponse: {
|
||||
id: innerId,
|
||||
name: innerName,
|
||||
response: responseMetadata,
|
||||
},
|
||||
};
|
||||
|
||||
const result = convertToFunctionResponse(
|
||||
toolName,
|
||||
callId,
|
||||
input,
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
);
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].functionResponse).toEqual({
|
||||
id: callId,
|
||||
name: toolName,
|
||||
response: responseMetadata,
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle llmContent as an array of multiple Parts (text and inlineData)', () => {
|
||||
const llmContent: PartListUnion = [
|
||||
{ text: 'Some textual description' },
|
||||
{ inlineData: { mimeType: 'image/jpeg', data: 'base64data...' } },
|
||||
{ text: 'Another text part' },
|
||||
];
|
||||
const result = convertToFunctionResponse(
|
||||
toolName,
|
||||
callId,
|
||||
llmContent,
|
||||
PREVIEW_GEMINI_MODEL,
|
||||
);
|
||||
expect(result).toEqual([
|
||||
{
|
||||
functionResponse: {
|
||||
name: toolName,
|
||||
id: callId,
|
||||
response: {
|
||||
output: 'Some textual description\nAnother text part',
|
||||
},
|
||||
parts: [
|
||||
{
|
||||
inlineData: { mimeType: 'image/jpeg', data: 'base64data...' },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle llmContent as an array with a single inlineData Part', () => {
|
||||
const llmContent: PartListUnion = [
|
||||
{ inlineData: { mimeType: 'image/gif', data: 'gifdata...' } },
|
||||
];
|
||||
const result = convertToFunctionResponse(
|
||||
toolName,
|
||||
callId,
|
||||
llmContent,
|
||||
PREVIEW_GEMINI_MODEL,
|
||||
);
|
||||
expect(result).toEqual([
|
||||
{
|
||||
functionResponse: {
|
||||
name: toolName,
|
||||
id: callId,
|
||||
response: { output: 'Binary content provided (1 item(s)).' },
|
||||
parts: llmContent,
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle llmContent as a generic Part (not text, inlineData, or fileData)', () => {
|
||||
const llmContent: Part = { functionCall: { name: 'test', args: {} } };
|
||||
const result = convertToFunctionResponse(
|
||||
toolName,
|
||||
callId,
|
||||
llmContent,
|
||||
PREVIEW_GEMINI_MODEL,
|
||||
);
|
||||
expect(result).toEqual([
|
||||
{
|
||||
functionResponse: {
|
||||
name: toolName,
|
||||
id: callId,
|
||||
response: {},
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle empty string llmContent', () => {
|
||||
const llmContent = '';
|
||||
const result = convertToFunctionResponse(
|
||||
toolName,
|
||||
callId,
|
||||
llmContent,
|
||||
PREVIEW_GEMINI_MODEL,
|
||||
);
|
||||
expect(result).toEqual([
|
||||
{
|
||||
functionResponse: {
|
||||
name: toolName,
|
||||
id: callId,
|
||||
response: { output: '' },
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle llmContent as an empty array', () => {
|
||||
const llmContent: PartListUnion = [];
|
||||
const result = convertToFunctionResponse(
|
||||
toolName,
|
||||
callId,
|
||||
llmContent,
|
||||
PREVIEW_GEMINI_MODEL,
|
||||
);
|
||||
expect(result).toEqual([
|
||||
{
|
||||
functionResponse: {
|
||||
name: toolName,
|
||||
id: callId,
|
||||
response: {},
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle llmContent as a Part with undefined inlineData/fileData/text', () => {
|
||||
const llmContent: Part = {}; // An empty part object
|
||||
const result = convertToFunctionResponse(
|
||||
toolName,
|
||||
callId,
|
||||
llmContent,
|
||||
PREVIEW_GEMINI_MODEL,
|
||||
);
|
||||
expect(result).toEqual([
|
||||
{
|
||||
functionResponse: {
|
||||
name: toolName,
|
||||
id: callId,
|
||||
response: {},
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getCitations', () => {
|
||||
it('should return empty array for no candidates', () => {
|
||||
expect(getCitations(minimalMockResponse(undefined))).toEqual([]);
|
||||
|
||||
Reference in New Issue
Block a user