mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-14 05:42:54 -07:00
refactor(core): extract static concerns from CoreToolScheduler (#15589)
This commit is contained in:
@@ -8,8 +8,125 @@ import type {
|
||||
GenerateContentResponse,
|
||||
Part,
|
||||
FunctionCall,
|
||||
PartListUnion,
|
||||
} from '@google/genai';
|
||||
import { getResponseText } from './partUtils.js';
|
||||
import { supportsMultimodalFunctionResponse } from '../config/models.js';
|
||||
import { debugLogger } from './debugLogger.js';
|
||||
|
||||
/**
|
||||
* Formats tool output for a Gemini FunctionResponse.
|
||||
*/
|
||||
function createFunctionResponsePart(
|
||||
callId: string,
|
||||
toolName: string,
|
||||
output: string,
|
||||
): Part {
|
||||
return {
|
||||
functionResponse: {
|
||||
id: callId,
|
||||
name: toolName,
|
||||
response: { output },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function toParts(input: PartListUnion): Part[] {
|
||||
const parts: Part[] = [];
|
||||
for (const part of Array.isArray(input) ? input : [input]) {
|
||||
if (typeof part === 'string') {
|
||||
parts.push({ text: part });
|
||||
} else if (part) {
|
||||
parts.push(part);
|
||||
}
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
export function convertToFunctionResponse(
|
||||
toolName: string,
|
||||
callId: string,
|
||||
llmContent: PartListUnion,
|
||||
model: string,
|
||||
): Part[] {
|
||||
if (typeof llmContent === 'string') {
|
||||
return [createFunctionResponsePart(callId, toolName, llmContent)];
|
||||
}
|
||||
|
||||
const parts = toParts(llmContent);
|
||||
|
||||
// Separate text from binary types
|
||||
const textParts: string[] = [];
|
||||
const inlineDataParts: Part[] = [];
|
||||
const fileDataParts: Part[] = [];
|
||||
|
||||
for (const part of parts) {
|
||||
if (part.text !== undefined) {
|
||||
textParts.push(part.text);
|
||||
} else if (part.inlineData) {
|
||||
inlineDataParts.push(part);
|
||||
} else if (part.fileData) {
|
||||
fileDataParts.push(part);
|
||||
} else if (part.functionResponse) {
|
||||
if (parts.length > 1) {
|
||||
debugLogger.warn(
|
||||
'convertToFunctionResponse received multiple parts with a functionResponse. Only the functionResponse will be used, other parts will be ignored',
|
||||
);
|
||||
}
|
||||
// Handle passthrough case
|
||||
return [
|
||||
{
|
||||
functionResponse: {
|
||||
id: callId,
|
||||
name: toolName,
|
||||
response: part.functionResponse.response,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
// Ignore other part types
|
||||
}
|
||||
|
||||
// Build the primary response part
|
||||
const part: Part = {
|
||||
functionResponse: {
|
||||
id: callId,
|
||||
name: toolName,
|
||||
response: textParts.length > 0 ? { output: textParts.join('\n') } : {},
|
||||
},
|
||||
};
|
||||
|
||||
const isMultimodalFRSupported = supportsMultimodalFunctionResponse(model);
|
||||
const siblingParts: Part[] = [...fileDataParts];
|
||||
|
||||
if (inlineDataParts.length > 0) {
|
||||
if (isMultimodalFRSupported) {
|
||||
// Nest inlineData if supported by the model
|
||||
(part.functionResponse as unknown as { parts: Part[] }).parts =
|
||||
inlineDataParts;
|
||||
} else {
|
||||
// Otherwise treat as siblings
|
||||
siblingParts.push(...inlineDataParts);
|
||||
}
|
||||
}
|
||||
|
||||
// Add descriptive text if the response object is empty but we have binary content
|
||||
if (
|
||||
textParts.length === 0 &&
|
||||
(inlineDataParts.length > 0 || fileDataParts.length > 0)
|
||||
) {
|
||||
const totalBinaryItems = inlineDataParts.length + fileDataParts.length;
|
||||
part.functionResponse!.response = {
|
||||
output: `Binary content provided (${totalBinaryItems} item(s)).`,
|
||||
};
|
||||
}
|
||||
|
||||
if (siblingParts.length > 0) {
|
||||
return [part, ...siblingParts];
|
||||
}
|
||||
|
||||
return [part];
|
||||
}
|
||||
|
||||
export function getResponseTextFromParts(parts: Part[]): string | undefined {
|
||||
if (!parts) {
|
||||
|
||||
Reference in New Issue
Block a user